- Dart 所有的东西都是对象,即使是数字、函数、null也都是对象。所有的对象都继承自 Object 类。
- Dart 动态类型语言, 尽量给变量定义一个类型,会更安全,没有显示定义类型的变量在 debug 模式下该类型是 dynamic(动态的)。
- Dart 在 running 之前解析你的所有代码,指定数据类型和编译时的常量,可以提高运行速度。
- Dart 没有public、private和protected关键字。如果标识符以下划线(_)开头,则该标识符对其库是私有的。
- 没有初始化的变量都会被赋予默认值 null。
- final的值只能被设定一次。const 是一个编译时的常量,可以通过 const 来创建常量值,var c=const[];这里 c 还是一个变量,只是被赋值了一个常量值,它还是可以赋其它值。
name的类型被推断为String:
var name = 'Bob';
如果对象不限于单一类型,或没有明确的类型,可用dynamic/Object:
Object name1 ='zf';
print('name1: ${name1.runtimeType}'); // String
name1 = ['a', 'b'];
print('name1: ${name1.runtimeType}'); // List<String>dynamic name2 ='zf';
print('name2: ${name2.runtimeType}'); // String
name2 =123;
print('name2: ${name2.runtimeType}'); // int显式声明变量的类型:
String name = 'Bob';
所有未初始化的变量默认值都是null
int lineCount;
// 在生产环境,assert()会被忽略。// 在开发环境,assert(condition)的condition不为真时抛出一个异常。assert(lineCount ==null);如果某个变量只打算设置一次值,请使用final或const修饰。final变量只能被初始化一次,const变量是一个编译时常量(const变量是隐式的final)。final修饰的变量在第一次声明的时候就要初始化。被final或者const修饰的变量,类型可以省略,但建议指定类型:
// 可以省略String这个类型声明final name0 ='Bob';
finalString name1 ='Bob';
const name2 ='Bob';
constString name3 ='Bob';被final或const修饰的变量值不能再修改。final或const不能和var同时使用。常量如果是类级别的,建议使用static const:
static const String name = 'Tom';
const关键字不只是声明常数变量,还可以用来创建常量值,以及声明创建常量值的构造函数。任何变量都可以有一个常量值:
// 注意:[]创建的是一个空的list集合// const []创建一个空的,不可变的列表var varList =const [];
final finalList =const [];
const constList =const [];
// 可以更改非final/const变量的值,即便它曾经具有const的值
varList = ['hahaha'];在常量表达式中,final/const的操作对象必须为bool、num、string或null,const常量必须用const类型的值初始化:
constString name0 ='a';
finalString name1 ='a';
constString name2 ='a';
const aConstList =const ['1', '2'];
// 报错:Const variables must be initialized with a constant valueconst validConstString0 ='$name0 $name1';
// 报错:In constant expressions, operands of this operator must be of type 'bool', 'num', 'String' or 'null'const validConstString1 ='$name0 $name2 $aConstList';
// 正确写法const validConstString2 ='$name0 $name2';- num: 是数字类型的父类,有两个子类 int 和 double
int a =1;
print(a);
double b =1.11;
print(b);
// String -> intint c =int.parse('1');
// String -> doubledouble d =double.parse('1.11');
// int -> StringString str1 = a.toString();
// 报错:The argument type 'int' can't be assigned to the parameter type 'String'print(str1 + a);
// 正确// print('${str1} ${a}');print(str1 + a.toString());
// double -> String,// toStringAsFixed()// 参数表示要保留几位小数,参数必填// 填写的整数值大于小数的小数点位数时,会补0// 返回结果四舍五入double num0 =3.141592653;
double num1 =3.14;
String str0 = num0.toStringAsFixed(0);
String str1 = num0.toStringAsFixed(3);
String str2 = num1.toStringAsFixed(4);
// str0 = 3, str1 = 3.142, str2 = 3.1400print('str0 = ${str0}, str1 = ${str1}, str2 = ${str2}');
// toStringAsExponential()// 科学计数,参数可为空,参数表示要保留几位小数,会四舍五入double num2 =12345.678;
String str3 = num2.toStringAsExponential();
String str4 = num2.toStringAsExponential(0);
String str5 = num2.toStringAsExponential(4);
// str3 = 1.2345678e+4, str4 = 1e+4, str5 = 1.2346e+4print('str3 = ${str3}, str4 = ${str4}, str5 = ${str5}');
// toStringAsPrecision()// 参数必填且>0, 表示有效数字的位数,也会四舍五入double num3 =123.654;
int num4 =1;
String str6 = num3.toStringAsPrecision(3);
String str7 = num3.toStringAsPrecision(1);
String str8 = num4.toStringAsPrecision(2);
// str6 = 124, str7 = 1e+2, str8 = 1.0print('str6 = ${str6}, str7 = ${str7}, str8 = ${str8}');- String: 可以使用单引号/双引号创建字符串;引号可以嵌套使用
String str1 ='aaaaa';
String str2 ='bbbbb';
int a =1;
String str3 ='$str1 a "b" ${str2.toUpperCase()} ${a.toString()}';- bool
- Dart 是强bool类型检查,只有bool类型的值是true,才被认为是true
- 只有两个对象具有bool类型:true和false,它们都是编译时常量
- assert 是语言内置的断言函数,仅在检查模式下有效。条件不为真则引发异常
var fullName ='';
assert(fullName.isEmpty);
var a =0/0;
assert(a.isNaN);
var b;
assert(b ==null);- List集合: 元素类型可以是任意类型
// 使用List的构造函数。如果传递int参数,表示List固定长度,不可增删操作List list0 =newList();
List list1 = ['a', 10, true, null, ['bala', 'heihei']];
// 添加一个元素
list1.add('apple');
// 添加多个元素
list1.addAll(['orange', 'banana']);
// 获取List的长度/首个元素/最后一个元素/指定下标元素/某个元素的索引print('${list1.length}, ${list1.first}, ${list1.last}, ${list1[0]}, ${list1.indexOf('apple')}');
// 删除指定位置的元素,返回被删除的元素print(list1.removeAt(0));
// 删除指定元素,返回true/false。如果集合中有多个相同元素,只会删除第一个
list1.remove('apple');
// 删除最后一个元素,返回被删除的元素print(list1.removeLast());
// 删除指定范围的元素,含头不含尾,没有返回值
list1.removeRange(0, 1);
// 删除指定条件的元素,没有返回值
list1.removeWhere((item) => item.runtimeType ==String);
// 删除所有的元素,没有返回值
list1.clear();- Map集合: map的键和值可以是任何类型的对象
// 这种方式初始化map时,key不能相同,否则报错:Two keys in a map literal can't be equal.Map map0 = {'name':'aaa', 'age':18, 111:true, null:null, ['a']: {'xxx':'yyy'}};
// 另一种创建方式Map map1 =newMap();
map1['name'] ='aaa';
map1['age'] =18;
// 指定键值对的参数类型。如果指定了key/value类型,赋值时必须为指定的类型Map map2 =newMap<int, String>();
// 对同一key进行赋值会被覆盖
map2[1] ='abc';
map2[1] ='aaa';
// map里面的value和key都可以是null
map2[2] =null;
// 检索Map是否含有某keyassert(map2.containsKey(1));
// 删除某个键值对(参数为key值)
map2.remove(1);- throw
抛出固定类型的异常
throw new FormatException('Expected at least 1 section');
抛出任意类型的异常
throw 'Out of memory!';
- on / catch
将可能出现异常的代码放到 try 语句中,可以通过 on 语句来指定需要捕获的异常类型;使用 catch 可以捕获到任意异常类型。rethrow可以重新抛出捕获的异常
test1() {
try {
test2();
} catch (e) {
print('test1 -- e -- ${e}');
}
}
test2() {
try {
throwFormatException('this is exception');
} onOutOfMemoryError { // 可以捕获指定类型的异常print('out of memory');
} onFormatExceptioncatch (e) { // 匹配不到FormatException,会继续匹配print('exception error: ${e}');
// 重新抛出异常,可以在test1的catch中重新捕获异常rethrow; } catch (e, r) { // 匹配所有类型的异常:e是异常对象,r时异常的堆栈信息print('exception details: ${e}');
print('stack trace: ${r}');
} finally { // 一定会执行,如果try/catch中有return/rethrow,会在return/rethrow之前执行print('this is finally');
}
}Dart 的函数有两种类型的参数:必需的和可选的。可选参数可以有一个默认值,在调用者没有指定可选参数时使用默认值。可选参数又分为:可选命名参数和可选位置参数。
- 可选命名参数: 使用{}包围的参数属于可选命名参数,可设置默认值,在调用的时候,需要使用paramName:value的形式指定为哪个可选参数赋值。
void_testPar1(int a, {String name, int age =10}) {
print('a = ${a}, name = ${name}, age = ${age}');
}
_testPar1(123, name:'abc'); // a = 123, name = abc, age = 10_testPar1(123, age:18); // a = 123, name = null, age = 18_testPar1(123, name:'aaa', age:18); // a = 123, name = aaa, age = 18- 可选位置参数: 使用[]包围的参数属于可选位置参数。如果想指定某个位置上的参数,必须保证前面位置有参数(设置了默认参数也需要传)。
void _testPar2 (int a, [String str ='abc', int b]) {
print('a = ${a}, str = ${str}, b = ${b}');
}
_testPar2(123); // a = 123, str = abc, b = null_testPar2(123, 'beijing'); // a = 123, str = beijing, b = null_testPar2(123, 'beijing', 10); // a = 123, str = beijing, b = 10_testPar2(123, 10); // 会报错classPoint {
int x;
int y;
// 构造函数Point(int a, int b) {
x = a;
y = b;
}
// 上面的构造函数可以简化为:Point(this.x, this.y);
// 命名构造函数Point.fromJson(Map data) {
x = data['x'];
y = data['y'];
}
// 可以在构造函数打括号前来初始化实例的变量值,多个实例变量用逗号隔开Point.fromJson(Map data) : x = data['x'], y = data['y'];
}Event-Looper
Dart 是单线程模型,也就没有所谓的主线程/子线程。 Dart 也是Event-Looper以及Event-Queue的模型,所有的事件都是通过EventLooper依次执行。 Dart 的Event Loop是:
- 从EventQueue中获取Event
- 处理Event
- 直到EventQueue为空
这些Event包括了用户输入,点击,Timer,文件IO等
单线程模型
Dart 中没有线程的概念,只有isolate,每个isolate都是隔离的,并不会共享内存。一个 Dart 程序是在Main isolate的main函数开始,在main函数结束后,Main isolate开始一个一个的处理Event Queue中的每一个Event。
Event Queue以及Microtask Queue
Dart 中的Main isolate只有一个Event Looper,但存在两个Queue:Event Queue和Microtask Queue。Microtask Queue存在的意义是:
希望通过这个Queue来处理稍晚一些,但是在下一个消息到来之前需要处理完的事情。
当Event Looper正在处理Microtask Queue中的Event时,Event Queue中的Event就停止了处理,此时App不能绘制任何图形,不能处理任何鼠标点击,不能处理文件IO等。
Event-Looper挑选Task的执行顺序为:
- 优先全部执行完Microtask Queue中的Event
- 直到Microtask Queue为空时,再执行Event Queue中的Event
Dart 中只知道Event处理的先后顺序,并不知道某个Event执行的具体时间点。
异步任务调度
当有代码可以在后续任务执行的时候,有两种方式,通过 dart:async 中的API即可
- 使用Future类,可以将任务加入到Event Queue的队尾
// 一般常用的Future构造函数newFuture(() { // do something }); // 当需要将任务拆分成很多小任务一步一步执行时,可以使用Future.then来拆解任务newFuture(() { print('abc'); return21; }).then((value) =>'!!!then01 $value!!!') //这里的value是上面任务的返回值 .then((value) { // 这里的value为上一个任务执行完的返回结果print('===then02 $value==='); // ===then02 !!!then01 21!!!===return'balabala'; }).then((value){ // 这里的value是上个任务的返回值print('~~~then03 $value~~~~'); // ~~~then03 balabala~~~~ }).whenComplete(() { print('complete'); }); // 当任务需要延时执行,可以使用Future.delay。不是绝对精准的。newFuture.delayed(constDuration(seconds:1), () {print('123');});
- 使用scheduleMicrotask函数,将任务加入到Microtask Queue队尾
import 'dart:async'; voidmain() { newFuture.delayed(constDuration(seconds:1), () {print('delay');}); print('main---111'); scheduleMicrotask(() { print('microtask---111'); }); newFuture(() { print('future'); return21; }).then((value) { print('future-then-$value'); }).whenComplete(() { print('complete'); }); scheduleMicrotask(() { print('microtask---222'); }); print('main---222'); }
上面代码的输出顺序为:
main---111 main---222 microtask---111 microtask---222 future future-then-21 complete delay
- 再看一个较复杂的例子
scheduleMicrotask(() =>print('000')); newFuture(() =>print('111')) .then((_) { print('222'); scheduleMicrotask(() =>print('333')); }).then((_) =>print('444')); scheduleMicrotask(() =>print('555')); newFuture(() =>print('666')) .then((_) { print('777'); Future(() =>print('888')); }).then((_) =>print('999')); newFuture(() =>print('aaa')); scheduleMicrotask(() =>print('bbb'));
上面输出顺序为:0-5-b-1-2-4-3-6-7-9-a-8
async和await
// async关键字声明该函数内部有延时执行的任务asyncAndAwait() async { // 在Dart中,有await修饰的,其结果是一个Future对象var a =awaittest0(); print('a-type:${a.runtimeType}'); print('a = ${a}'); var b =awaittest1(); print('b-type:${b.runtimeType}'); print('b = ${b}'); } test0() { returnFuture(() =>'22222'); } test1() { returnFuture(() =>'22222') .then((value) { print('then-value=${value}'); }).whenComplete(() { print('complete~'); }); }
上面代码的输出顺序依次为:
a-type:String a = 11111 then-value=22222 complete~ b-type:Null b = null
在 Flutter 中,一切能显示的都是 Widget 。 Widget 是一切的基础,作为响应式的渲染,属于 MVVM 的实现机制。我们可以通过修改数据,再用 setState 设置数据, Flutter 会自动通过绑定的数据更新 Widget 。我们需要做的就是实现 Widget 页面,并且和数据绑定起来。
Widget 分为 有状态 和 无状态 两种。在 Flutter 中每个页面都是一帧。无状态就是保持在那一帧;有状态的 Widget 当数据更新时,其实就是绘制了新的 Widget 。
无状态 StatelessWidget
无状态的 Widget 实现很简单:创建一个继承自 StatelessWidget 的 Widget ,并通过 build 方法返回一个布局好的控件。Widget 和 Widget 之间通过 child 或 children 进行嵌套。
import'package:flutter/material.dart';
classMyStatelessextendsStatelessWidget {
finalString text;
MyStateless(this.text);
@overrideWidgetbuild(BuildContext context) {
returnScaffold(
appBar:AppBar(
title:Text('StatelessWidget'),
),
body:Container(
color:Colors.white,
//Dart语法中,?? 表示如果text为空,就返回 ?? 后的内容。
child:Text(text ??"这就是无状态DMEO"),
),
);
}
}- 有状态 StatefulWidget
有状态的 Widget 需要管理 state ,通过 setState 改变数据触发 Widget 重新构建刷新。主要的生命周期有:
initState: 初始化 didChangeDependencies: 在 initState 之后 build 之前调用 dispose: 销毁
classMyStatefulextendsStatefulWidget {
@overrideState<StatefulWidget> createState() {
return_MyStatefulState();
}
}
class_MyStatefulStateextendsState<MyStateful> {
int pressCount;
@overridevoidinitState() {
super.initState();
pressCount =0;
}
@overrideWidgetbuild(BuildContext context) {
returnContainer(
child:FlatButton(
onPressed: () {
setState(() {
pressCount +=1;
});
},
child:Center(
child:Text('有状态的Widget,可戳👇--$pressCount'),
)
),
);
}
}不管是有状态的还是无状态的 Widget ,我们需要做的就是在 build 中堆积我们的布局,把数据添加到 Widget 中,有状态的可以通过 setState 改变数据,从而刷新UI,就这么简单。
先来看一张图,这张图中几乎包含了 Flutter 中所有的 Widget:
我们先针对其中比较常用的讲解下其特性和使用方法。
Container
最常用的布局。只能包含一个 child ,默认铺满。支持配置 padding 、margin 、color 、宽高、decoration (一般配置边框和阴影等)。不是所有的控件都有 padding 、margin 、color 、宽高等属性,所以才会有 Padding 、Center 等 Widget 的存在。
alignment: 设置Container内child的对其方式(注意不是Container本身的对其方式)
width/height/color: 设置Container的宽高和背景色
padding/margin: padding设置Container边缘和其child内容的距离,margin设置Container和其它外部Widget的距离。
decoration: Container的修饰器,主要设置背景、边框、圆角等。注意:如果设置了该属性,就不能设置Container的color,否则会抛出异常
_containerDemo() { returnContainer( alignment:Alignment.center, child:Text('balabalabala'), height:49, margin:EdgeInsets.all(20), padding:EdgeInsets.only(top:10), // color: Colors.blueGrey,// 使用它就不能设置Container的color decoration:BoxDecoration( borderRadius:BorderRadius.only( topLeft:Radius.circular(10), bottomRight:Radius.circular(10) ), color:Colors.blue, border:Border.all( color:Color(0xFF000000), width:1 ) ), ); }
Column、Row和Expanded
竖直布局和水平布局也是很常见的场景。它们都可以通过 children 设置多个子 Widget ,可以设置主轴和副轴方向布局方式等。而 Expended 在 Column 和 Row 中默认代表着平均充满。当然可以通过设置 flex 属性决定占屏比例。
_columnDemo() { returnColumn( children:<Widget>[ Expanded(child:Container( child:Text('aaa'), color:Colors.grey, )), Expanded(child:Container( child:Text('bbb'), color:Colors.blueGrey, )), Expanded( child:Container( child:Text('ccc'), color:Colors.red ), flex:2 ) ], // 主轴方向布局方式 mainAxisAlignment:MainAxisAlignment.center, // 大小按照最大充满(这里使用Expanded会最大充满不受该属性设置影响) mainAxisSize :MainAxisSize.min, // 副轴方向布局方式 crossAxisAlignment:CrossAxisAlignment.center ); }
Flutter 中除了布局的 Widget ,还有交互显示和完整页面呈现的 Widget 。其中常见的有 MaterialApp 、Scaffold 、Appbar 、Text 、Image 、FlatButton 等。
Text
textAlign: 设置文本对其方式:center、left、right、start、end
maxLines: 设置最多显示的行数
overflow: 文本溢出时怎么处理。clip:直接切断;ellipsis:...;fade:渐变(上下渐变,不是左右)
style: 通过TextStyle设置字体大小、颜色、weight等
_textDemo() { returnContainer( width:100, child:Text( 'balabalabalabalabalabalabalabalabalabalabalabalabalabalabala', textAlign:TextAlign.end, style:TextStyle( fontSize:20, color:Colors.white, fontWeight:FontWeight.w500 ), maxLines:1, overflow:TextOverflow.fade, ) ); }
image
加载图片有四种方式分别为:
Image.asset: 加载项目资源目录中的图片,相对路径
Image.network: 加载网络图片
Image.file: 加载本地图片,绝对路径
Image.memory: 加载Uint8List资源图片(没使用过😢)
通过fit属性设置图片的填充方式:
BoxFit.fill: 图片拉伸以充满父容器
BoxFit.contain: 比例不变拉伸,可能会有空隙(水平和竖直方向最多有一个方向有空隙)
BoxFit.cover: 可能被裁减,比例不变拉伸,充满父容器
BoxFit.fitWidth/fitHeight: 宽度/高度充满
BoxFit.scaleDown: 效果和contain类似,但图片不会被拉伸超过源图片大小
_imageDemo() { returnContainer( alignment:Alignment.center, width:300, height:400, color:Colors.blue, child:Image.network( 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2527385882,2413489660&fm=26&gp=0.jpg', fit:BoxFit.contain, ) ); }
TextField
TextField 和 CupertinoTextField 是两种不同风格的 textField ,两者样式有很大差异,用法略有不同。先来看下 TextField 的基本使用:
_textFieldDemo0() { returnTextField( maxLength:30, maxLines:1, // 是否自动聚焦 autofocus:false, // 是否为密码输入框 obscureText:false, textAlign:TextAlign.left, style:TextStyle( fontSize:17, color:Colors.black ), onChanged: (text) { print('onChanged:--$text--'); }, onSubmitted: (text) { print('onSubmitted:--$text--'); }, // 装饰效果 decoration:InputDecoration( // decoration是否使用fillColor填充,默认为false filled:false, fillColor:Colors.red, hintText:'placeholder', helperText:'helperText', labelText:'labelText' ), enabled:true, // 在onSubmitted之前调用。实现该方法后,点键盘return输入框不会失去焦点 onEditingComplete: () { print('TextField:complete'); } ); }
下面 CupertinoTextField (iOS风格的TextField)的基本使用:
_textFieldDemo1() { returnCupertinoTextField( maxLength:200, maxLines:1, autofocus:false, // 密码 obscureText:false, autocorrect:false, textAlign:TextAlign.left, style:TextStyle( fontSize:17, color:Colors.black ), onChanged: (text) { print('onChanged:--$text--'); }, onSubmitted: (text) { print('onSubmitted:--$text--'); }, placeholder:'placeholder', clearButtonMode:OverlayVisibilityMode.editing, enabled:true, // 设置光标宽度,默认为2 cursorWidth:2, // 设置光标颜色,默认为blue cursorColor:Colors.red, // 在onSubmitted之前调用。实现了该方法后,按return不会让textField失去焦点 onEditingComplete: () { print('CupertinoTextField:complete'); } ); }

RaisedButton & FlatButton
FlatButton用法和RaisedButton基本相同,但样式上FlatButton属于扁平化,RaisedButton属于拟物化,有立体感(凸起),可以设置Button底部阴影效果。
// RaisedButton 凸起效果_raisedButtonDemo() { returnRaisedButton( onPressed: () =>_showMyDialog(), // highlight状态发生变化就调用(高亮和取消高亮都调用) onHighlightChanged: (value) { print('highlight changed: ${value}'); }, // 文本文字颜色 textColor:Colors.white, // 背景色 color:Colors.blue, child:Text('戳'), padding:EdgeInsets.all(10), shape:RoundedRectangleBorder( borderRadius:BorderRadius.circular(20), side:BorderSide( color:Colors.black, width:1 ) ), elevation:10, ); }
OutlineButton
线框按钮,它的color属性不会生效(即不能通过color设置OutlineButton的背景色),带有默认边框。其余用法和效果同FlatButton,都是扁平化的效果。
// OutlineButton 扁平化,默认带边框_outlineButtonDemo() { returnOutlineButton( onPressed: () =>_showMyDialog(), textColor:Colors.blue, child:Text('balabala'), padding:EdgeInsets.all(10), // borderSide可以设置边框的颜色、宽度和边框样式(none和solid两种) borderSide:BorderSide(color:Colors.blue), shape:RoundedRectangleBorder( borderRadius:BorderRadius.circular(20), // 这里的BorderSide只可以设置边框样式,并且优先级低 side:BorderSide( style:BorderStyle.solid ) ) ); }
RawMaterialButton
其效果类似FlatButton,用法与上面的三种button稍有差异。上面介绍的几种button,它们的很多属性(如shape)可以设置为null,但RawMaterialButton的属性不允许有null,否则抛出异常。
// MaterialButton 扁平化,效果类似FlatButton_rawMaterialButtonDemo(bool withBorder) { returnRawMaterialButton( onPressed: () =>_showMyDialog(), child:Text('balabala'), padding:EdgeInsets.all(10), // 填充色/背景色 fillColor:Colors.blue, textStyle:TextStyle(color:Colors.white), shape:RoundedRectangleBorder( borderRadius:BorderRadius.circular(20), side:BorderSide( style:BorderStyle.solid ) ) ); }
⚠️ 以上这几种button都可以设置其padding值,但它们都有一个默认的padding。当这些button的上一层级Widget的尺寸足够容纳button,这时设置的padding值比默认值大才会生效。当button上一层级的Widget不足以展示button的默认padding时,设置较小的padding可以生效。我们可以通过在button外面包裹一层Container,并设置其alignment为center,再根据需求设置Container的宽高,就可以布局出自己想要的button样式。通过GestureDetector实现自定义样式的button
由于上面介绍的几种button,都有一些无法改变的固定样式,这往往不能满足开发中的特定需求。除了上面说的利用Container包裹button以达到想要的样式之外,还可以通过GestureDetector来实现自定义样式的button(其实就是在自己布局的Widget基础上添加了点击手势功能)。
_myButton() { returnGestureDetector( onTap: () =>_showMyDialog(), child:Container( width:70, height:28, margin:EdgeInsets.only(top:20), decoration:BoxDecoration( borderRadius:BorderRadius.circular(11.0), border:Border.all(width:0.5, color:Color(0xFF6BCBD7)) ), child:Center( child:Text('点击刷新', style:TextStyle( fontSize:13, color:Color(0xFF6BCBD7) ), ), ), ), ); }
CupertinoButton
CupertinoButton是iOS风格的button
_iosButtonDemo() { returnContainer( margin:EdgeInsets.only(top:15), child:CupertinoButton( child:Text( 'abc', style:TextStyle(fontSize:17, color:Colors.white), ), color:Colors.blue, minSize:20, padding:EdgeInsets.fromLTRB(15, 10, 15, 10), borderRadius:BorderRadius.circular(10), onPressed: () =>_showMyDialog() ) ); } // 以上所有例子中的按钮点击事件_showMyDialog() { showDialog( context: context, builder: (context) { returnAlertDialog( title:Text('我是title'), content:Text('我是content'), actions:<Widget>[ FlatButton( onPressed: () { Navigator.pop(context); }, child:Text('确定') ), FlatButton( onPressed: () { Navigator.pop(context); }, child:Text('取消') ) ], ); } ); }






