Future再论:
这里在继续往下学习之前,先来看一下Dart语言关于Event-Queue和Microtask Queue需要注意的一个小点,这个在之前https://www.cnblogs.com/webor2006/p/11994645.html已经学习过了,先来回顾一下当时的代码:
import 'dart:async'; void main() { testScheduleMicrotask(); } void testScheduleMicrotask() { scheduleMicrotask(() => print('s1')); new Future.delayed(new Duration(seconds: 1), () => print('s2')); new Future(() => print('s3')).then((_) { print('s4'); scheduleMicrotask(() => print('s5')); }).then((_) => print('s6')); new Future(() => print('s7')); scheduleMicrotask(() => print('s8')); print('s9'); }
输出结果为:
好,接下来再来改一下:
那又输出啥呢?这里先看结果:
其中相比上面的输出,就是这块不同:
那咱们就针对性的分析加入的这代码的逻辑:
正常来理解应该是s10、s11、s12、s7嘛,为啥输出的跟预想的不一样的,其实是这样的:
所以此时就只会输出s10了,而接下来则会执行下面的这个Future了:
接下来则这块相继会得到执行:
所以结果就是s10、s7、s11、s12了。
好,继续再来改,彻底要理解透这块的东东:
其实很好理解,这句会导致之后的then都不会打印,所以是先打印s3,然后再打印下面的s10,最终如下:
记住一点,then中如果是一个Future则不会立马执行了就成。
常用组件:
继续来学Flutter的常用组件,组件太多,挑常用的先学着,这块得要通过项目来实战才行,但是不了解基础用法也不行。
文本组件:
Text:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("Text示例"), ), body: Center( child: Column( children: <Widget>[ Text( 'tttttttttttttttttttttttttttkkkkkkkkkkkkkkkkssssssssssssstestlkdkdkkd'), ], ), ), ), ); } }
咱们可以将它居中显示:
TextOverflow:单行处理
如果只想显示单行,则有几种形式显示:
而还可以省略显示:
它则是底部有一层阴影效果:
TextStyle:
设置删除线:
其中这个删除线的样式也能改呢?
RichText:
其实在Andorid中也有类似的东东,就是在一断文本中可以增加一些点击事件之类的,下面来看一下如何弄:
接下来处理一下点击事件,这里点击之后打开一个网页,先来添加一个依赖包:
这里肯定需要用到异步处理了,如下:
运行看一下效果:
其中这是啥语法呢?
图标及按钮组件:
图标组件Icon展示图标的组件,该组件不可交互,要实现交互图标,可以考虑使用IconButton组件。图标相关组件有以下几个:
- Icons:框架自带Icon集合
这个在之前也已经多次用过了:
- IconButton:可交互的Icon,图标按钮组件IconButton是基于Meterial Design风格的组件,可以响应按下的事件,并且按下时带水波纹效果。如果它的onPressed回调函数为null,那么这个按钮处于禁用状态,并且不可按下。
其中点击时用了一个三方的toast库,所以需要添加一下依赖:
效果:
- RaisedButton:是Material Design中的button,一个凸起的材质矩形按钮,它可以响应按下事件,并且按下时会带一个触摸效果。
列表控件:
列表是最常见的需求。在Flutter中,用ListView来显示列表项,支持垂直和水平方向展示,通过一个属性我们就可以控制其方向,列表有以下几类。
基础列表组件:
基础列表组件为ListView组件。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("listview示例"), ), body: ListView( children: <Widget>[ ListTile( leading: Icon(Icons.favorite_border), title: Text('favarite_border'), ), ListTile( leading: Icon(Icons.favorite_border), title: Text('favarite_border'), ), ListTile( leading: Icon(Icons.favorite_border), title: Text('favarite_border'), ), ListTile( leading: Icon(Icons.favorite_border), title: Text('favarite_border'), ), ], ), ), ); } }
就不过多解释了,根据效果就能知道上面写的代码的含义,下面再来扩展一下:
可以还差分隔线,可以修改如下:
这里介绍一个小技巧,可以通过IDE来抽象出一个Widget来,如下:
水平列表组件:
水平列表组件即为水平方向排列的组件,列表内部元素以水平方向排列。把ListView组件的scrollDirection属性设置为Axis.horizontal即可。
注意一下这种是一次性加载100条,而非按需加载,为啥要提这个,因为之后还有一种创建方式。这里又把它抽取一下:
长列表组件【最常用】:
当列表的数据项非常多时,需要使用长列表,比如淘宝后台订单列表、手机通讯录等,这些列表项数据很多。长列表也是使用ListView作为基础组件,只不过需要添加一个列表项构造器itemBuilder。
很明显就是按需来加载的,也就是有别于上面使用的List.generate()生成的列表,这里给列表中增加一个下划线:
网络布局组件:
网格布局组件GridView可以实现多行多列布局的应用场景。使用GridView创建网格列表有多种方式:
- GridView.count:通过单行展示个数创建。
先运行看一下:
可以看到总共是2行,然后是横向滑动的。其中childAspectRatio它是啥意思呢?在网上找了一下:其实就是每个隔子高度与宽度的比例,如目前我们设置的是2/3,则高度占2,宽度占3,所以是一个长方形,这里为了看到效果,设置为3行:
如果设置成3/3,那很显然就成一个正方形了:
而如果设置成1/3,很显然是更加长了:
接着我们还可以设置成垂直滚动的:
而此时的childAspectRatio设置的则是子元素宽和高的比例,跟那个横向滑动是相反的。 - GridView.builder():
跟ListView一样,也可以用Builder来进行构建:
表单组件:
表单是一个包含表单元素的区域。表单元素允许用户输入内容,比如文本域,下拉列表,单选框,复选框等。常见应用场景有:登录、注册、输入信息等。表单里有两个重要的组件,一个是Form组件,用来左整个表单提交使用的;另一个是TextFormField组件,用来做用户输入的。
Form组件常用属性:
说到表单,当然最典型的就是用户登录了,所以下面来编写一个用户登录的表单界面,这里直接贴出代码:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("gridview示例"), ), // body: new GridViewWidget()), body: LoginPage()), ); } } class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { String _userName; String _password; @override Widget build(BuildContext context) { return MaterialApp( home: Column( children: <Widget>[ Container( padding: const EdgeInsets.all(10.0), //间隙 child: Form( child: Column( children: <Widget>[ TextFormField( decoration: InputDecoration(labelText: '请输入用户名'), onSaved: (value) { print('$value'); _userName = value; }, onFieldSubmitted: (value) { print('onFieldSubmitted : $value'); }, ), TextFormField( decoration: InputDecoration(labelText: '请输入密码'), onSaved: (value) { print('$value'); _password = value; }, validator: (value) { //这里做个校验,如果不足6位给出错误提示 return value.length < 6 ? "密码长度不够6位" : null; }, ), Container( margin: const EdgeInsets.only(top: 20.0), 400.0, height: 40.0, child: RaisedButton( child: Text('登录'), onPressed: () { //TODO 进行登录 }, ), ) ], ), ), ) ], ), ); } }
运行一下:
此时则需要来处理点击事件了,这里面有个写法比较陌生,先记住,之后还会再学的:
运行看一下效果:
此时日志输出如下:
另外,密码框现在还是明文的,得搞成密文风格:
Material Design风格组件:
Material Design是由谷歌推出的全新设计语言,这种设计语言旨在为手机、平板电脑、台式机和其他平台提供更一致、更广泛的外观和感觉。Material Design风格是一直非常有质感的设计风格,并会提供一些默认的交互动画,对于搞Android开发的来说应该耳熟能详了。
App结构和导航组件:
MaterialApp(应用组件):
MaterialApp代表使用Material Design风格的应用,里面包含了其他所需的基本控件。官方提供的示例demo就是从MaterialApp这个主组件开始的,我们在之前也已经使用多次了,这里再来从头梳理一下它:
设置主页:
使用home属性设置应用的主页,即整个应用的主组件。
路由处理:
routes对象是一个Map<String, WidgetBuilder>。当使用Navigator.pushNamed来跳转路由的时候,通过routes查找路由名字,然后使用对应的WidgetBuilder来构造一个带有页面切换动画的MaterialPageRoute。如果应用只有一个界面,则不用设置整个属性,使用home即可。
下面来使用一下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( routes: {'/other': (BuildContext context) => OtherPage()}, home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MaterialApp示例'), ), body: Center(), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.pushNamed(context, '/other'); }, tooltip: '路由跳转', foregroundColor: Color(0xffffffff), backgroundColor: Color(0xff000000), //阴影 elevation: 0.0, child: Icon(Icons.arrow_forward), ), ); } } class OtherPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('OtherPage'), ), ); } }
此时运行就可以实现页面的跳转了:
这里使用了FloatingActionButton,顺便再来学一下它的其它用法,还可以控制它的位置:
还可以将圆形变为方形:
自定义主题:
应用程序的主题,各种定制的颜色都可以设置,用于程序主题切换。 这个在未来用到时再进一步学习。
Scaffold(脚手架组件):
Scaffold实现了基本的Material Design布局。只要是在Material Design中定义过的单个界面显示的布局组件元素,都可以使用Scaffold来绘制。
AppBar(应用按钮组件):
应用按钮组件有AppBar和SliverAppBar。它们是Material Design中的AppBar,也就是Android中的ToolBar。
AppBar和SliverAppBar都继承自StatefulWidget,两者的区别在于AppBar的位置是固定在应用最上面的;而SliverAppBar是可以跟随内容滚动的。
BottomNavigatorBar(底部导航栏):
这个就是典型的像微信那种底部的菜单TAB效果,下面来实现一下:
class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { int _currentIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MaterialApp示例'), ), body: Center(), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.pushNamed(context, '/other'); }, tooltip: '路由跳转', foregroundColor: Color(0xffffffff), backgroundColor: Color(0xff000000), //阴影 elevation: 0.0, child: Icon(Icons.arrow_forward), // shape: RoundedRectangleBorder(), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem( icon: Icon(Icons.message), title: Text('信息'), ), BottomNavigationBarItem( icon: Icon(Icons.contacts), title: Text('通讯录'), ), BottomNavigationBarItem( icon: Icon(Icons.near_me), title: Text('发现'), ), BottomNavigationBarItem( icon: Icon(Icons.account_circle), title: Text('我'), ), ], currentIndex: _currentIndex, onTap: (index) { setState(() { _currentIndex = index; }); }, ), ); } }
运行看一下:
再运行:
哇塞,效果还蛮好的呢?下面点击完之后,咱们在上面切换一下内容,这里简单切换一下文件模拟下:
再运行一下:
关于Material Design风格组件还有很多,下次再继续。