Aqua

Utility classes/functions to help with UI development using the Flutter framework.

建议在导入包时使用 as 关键字,以避免名称冲突,例如...

import 'package:aqua/aqua.dart' as aqua

将输出日志记录到文件

await aqua.log('data to log', logFile: 'path/to/file', clear: true, time: true);

将数据保存到设备

Future<void> save() async {
	var id = 'id';
	var _value = 1920;
	await aqua.Pref.set(id, value);
	var value = await aqua.Pref.get(id);
}

将代码块放在 try catch 中


// the argument for tryCatch could either be a callback to a function or a future

// as a function
Widget  _buildWidget(){
	// may fail to build for some reason
}
var child = await aqua.tryCatch(_buildWidget());

// as a future
Future<Widget> _buildWidget() async {
	// may fail to build for some reason
}

var future = _buildWidget();
var child = await aqua.tryCatch(future);


if(child != null){
	// proceed
}

在从服务器获取 json 数据或向服务器发送 json 查询时

// posting to an endpoint
var fromServer = await aqua.Client(
	'192.168.1.100', //ip
	8080, // port
	'/test', // endpoint
	query: {
		'id': 1,
	}, // post parameters
	// verbose: true
).getResponse();

// a real end point would look like this
// the end point is free to use
// you can find the rest of the endpoints here: https://www.coingecko.com/en/api
var fromServer = await aqua.Client(
	'api.coingecko.com',
	443,
	'/api/v3/coins/markets',
	isSecured: true,
	query: {
		'vs_currency': 'usd',
		'order': 'market_cap_desc',
		'per_page': '20',
		'page': '1',
		'sparkline': 'true'
	}
).getResponse(method: 'GET');

仅限桌面 允许鼠标指针在悬停在可点击的小部件上时更改为点击图标

@override
Widget build(BuildContext context){
	
	// some code
	// then
	return aqua.MouseInteractivity(
		child: child
	);
}

在一定范围的索引之间获取随机索引

var index = aqua.getRandomNumber(min: 10, max: 10000);

用不同的颜色在屏幕上输出信息


aqua.pretifyOutput('to print on screen'); // will print in green
aqua.pretifyOutput('to print on screen', color: 'red');

为图片添加阴影效果

@override
Widget build(BuildContext context){
	return Stack(
		children: [
			_buildImage(),
			aqua.Shadow(
				width: width,
				height: height,
			)
		]
	);
}

一个快速的下拉小部件

@override
Widget build(BuildContext context){
	return aqua.DropDown(
		initValue: 'one',
		items: ['one', 'two', 'three', 'four', 'five', 'six']
	);
}

快速的 TabBar。没有脚手架的 Tab...

@override
Widget build(BuildContext context){
	return Material(
		child: DefaultTabController(
			length: 3,
			child: Column(
				children: [
					aqua.TabHeader(
						tabListing: ['car', 'transit', 'bike'],
					),

					Expanded(
						child: TabBarView(
							children: [
								Icon(Icons.directions_car),
								Icon(Icons.directions_transit),
								Icon(Icons.directions_bike),
							],
						),
					)
				],
			),
		)
	);
}

按比例获取屏幕尺寸

@override
Widget build(BuildContext context){

	aqua.Dimensions().init(context);

	return Container(
		width: aqua.Dimensions.width, // full width of screen
		height: aqua.Dimensions.height, // full height of screen
		color: Colors.red
	);
}

大写一个单词

var capitalized = aqua.capitalize('alphabet');
print('capitalized: $capitalized);

快速创建一个文件(还会创建路径上的递归目录)

await aqua.createFile('/path/to/file');

// to clear a file/truncate a file
await aqua.createFile('/path/to/file', clear: true);

生成随机 ID

var id = aqua.generateUUID(length: 30);

导航


// some code
// then

aqua.Dimensions().init(context);
Widget viewOne = Container(
	width: aqua.Dimensions.width,
	height: aqua.Dimensions.height
	color: Colors.blue
);
Widget viewTwo = Container(
	width: aqua.Dimensions.width,
	height: aqua.Dimensions.height
	color: Colors.red
);

// to navigate to the next view without erasing the previous view from state
aqua.CustomNavigator(
	context: context,
	buildScreen: () = > viewOne
).navigateToPage();

// to navigate to the next view and erase the previous view from state
aqua.CustomNavigator(
	context: context,
	replaceSingle: true,
	buildScreen: () = > viewOne
).navigateToPage();

// to navigate to the next view and erase ALL the previous views from state
aqua.CustomNavigator(
	context: context,
	replaceAll: true,
	buildScreen: () = > viewOne
).navigateToPage();

// to navigate to the next view using a named route
aqua.CustomNavigator(
	context: context,
	namedRoute: '/home',
	buildScreen: () = > viewOne
).navigateToPage();

显示加载图标


// render this widget as you see fit
Widget loadingIcon = aqua.LoadingIcon();

// to specify a loading type: check the different loading icons at this location
// https://github.com/jogboms/flutter_spinkit

import 'package:flutter_spinkit/flutter_spinkit.dart' as spinkitt;

Widget loadingIcon = aqua.LoadingIcon(
	spinkitWidget: spinkitt.SpinKitWave(
		color: Colors.blue,
		size: 40.0,
	),
);

请求另一小部件的焦点

@override
Widget build(BuildContext context){
	
	// some code
	// then
	Widget withFocus = aqua.requestFocus(child, context: context);

	// make sure to return a widget
}

为数字添加逗号

String number = aqua.pretifyNumber('1000000');
print('number: $number');

将图片裁剪成圆形小部件

@override
Widget build(BuildContext context){
	// some code
	// then
	Widget clippedImage = aqua.ClippedCircle(
		child: child // some widget, could be an image, wrapped in a container,
		color: Colors.blue // border of the circle
	);

	// make sure to return a widget
}

自定义文本表单小部件


class Play extends StatefulWidget {

	@override
	PlayState createState() => PlayState();

}

class PlayState extends State<Play>{

	FocusNode focusNode;
	TextEditingController textEditingController;

	@override
	void initState(){
		super.initState();

		focusNode = FocusNode();
		textEditingController = TextEditingController();
	}

	@override
	void dispose(){
		focusNode?.dispose();
		textEditingController?.dispose();

		super.dispose();
	}

	@override
	Widget build(BuildContext context){
		aqua.Dimensions().init(context);

		// more on this later ... 
		// check source code :)

		return SizedBox(
			width: aqua.Dimensions.width,
			height: 100.0,
			child: aqua.TextFormFieldCustom(
				isOutlineBorder: true, // or false
				focusNode: focusNode
				controller: textEditingController,
			)
		);
	}

}

Flutter 设备 窗口导航器,需要一些样板代码才能使其工作...

要设置您的移动环境,请点击此处
要设置您的桌面环境,请点击此处

复制下面的代码以渲染如下屏幕

window_nav

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:aqua/aqua.dart' as aqua;

class Shell extends StatefulWidget {

	@override
	ShellState createState() => ShellState();
}

class ShellState extends State<Shell>{

	Widget selectedWidget;
	aqua.NavigationStreamer mainNavStreamer;
	StreamSubscription mainNavStreamSubscription;

	@override
	void initState(){
		super.initState();
		mainNavStreamer = aqua.NavigationStreamer();

		mainNavStreamSubscription = mainNavStreamer.listen((data){
			aqua.pretifyOutput('[SHELL] data from nav stream: $data');

			selectedWidget = data['window'];
			setState((){});
		});
	}

	@override
	void dispose(){
		mainNavStreamSubscription.cancel();
		mainNavStreamer.close();
		super.dispose();
	}

	Widget _buildShell(BuildContext context){
		aqua.Dimensions().init(context);
		double navWidth = aqua.Dimensions.width * 0.15;
		double contentWindowWidth = aqua.Dimensions.width - navWidth;

		Map<String, Map<String, dynamic>> generatedRoutes = _buildGeneratedRoutes(
			contentWindowWidth,
			aqua.Dimensions.height
		);

		var textStyle = TextStyle(
			fontSize: 30.0,
			color: Colors.white,
			fontWeight: FontWeight.bold
		);

		Widget firstWidget = Container(
			width: contentWindowWidth,
			height: aqua.Dimensions.height,
			color: Colors.purple,
			child: Center(
				child: Text(
					'Home',
					style: textStyle
				)
			),
		);

		return Scaffold(
			appBar: null,
			body: SingleChildScrollView(
				child: Container(
					child: Row(

						children: [

							aqua.Navigation(
								width: navWidth,
								height: aqua.Dimensions.height,
								header: Container(
									width: navWidth,
									height: 100.0,
									color: Colors.red,
									child: Center(
										child: Text(
											'Header',
											style: TextStyle(
												color: Colors.white,
												fontWeight: FontWeight.bold
											),
										),
									),
								),
								routes: generatedRoutes,
								bgColors: <Color>[
									Colors.blue,
									Colors.blueAccent
								],
								hoverColor: Colors.brown.withOpacity(0.5),
								selectedColor: Colors.white.withOpacity(0.5),
								navStreamer: mainNavStreamer,
							),

							Container(
								child: Column(
									mainAxisAlignment: MainAxisAlignment.start,
									children: [
										selectedWidget == null ? aqua.requestFocus(
											firstWidget,
											context: context
										) : aqua.requestFocus(
											selectedWidget,
											context: context
										)
									],
								),
							)
						]
					)
				),
			),
		);
	}

	@override
	Widget build(BuildContext context) => _buildShell(context);

	Map<String, Map<String, dynamic>> _buildGeneratedRoutes(double windowWidth, double windowHeight){

		var textStyle = TextStyle(
			fontSize: 30.0,
			color: Colors.white,
			fontWeight: FontWeight.bold
		);

		Function _buildIconHelper = (IconData iconData){
			return Icon(iconData, color: Colors.black, size: 15.0,);
		};

		return {
			'Home': {
				'window': Container(
					width: windowWidth,
					height: windowHeight,
					color: Colors.purple,
					child: Center(
						child: Text(
							'Home',
							style: textStyle
						)
					),
				),
				'icon': _buildIconHelper(Icons.home)
			},
			'Search': {
				'window': Container(
					width: windowWidth,
					height: windowHeight,
					child: Stack(
						children: [
							aqua.Shadow(
								height: windowWidth,
								width: windowHeight,
								colors: [
									Colors.green,
									Colors.teal
								],
							),
							Center(
								child: Text(
									'Search',
									style: textStyle
								)
							),
						],
					)
				),
				'icon': _buildIconHelper(Icons.search)
			},
			'Settings': {
				'window': Container(
					width: windowWidth,
					height: windowHeight,
					color: Colors.indigo,
					child: Center(
						child: Text(
							'Settings',
							style: textStyle
						)
					),
				),
				'icon': _buildIconHelper(Icons.settings)
			}
		};
	}
}

GitHub

https://github.com/therenca/aqua