Flutter 是一个跨平台的 UI 工具集,它的设计初衷,就是允许在各种操作系统上复用同样的代码,例如 iOS 和 Android,同时让应用程序可以直接与底层平台服务进行交互。如此设计是为了让开发者能够在不同的平台上,都能交付拥有原生体验的高性能应用,尽可能地共享复用代码的同时,包容不同平台的差异。
1.Dart 1.基础 1.1入口方法
main.dart
1.1注释
//
/**/
///
1.3定义变量
var strs= “dart”; print(strs);
1.4声明变量
字符串
String str=”dart”;
数字类型
int num = 10;
1.5修饰符
var
final 只能赋值一次不能修改
const 一开始就赋值 常量不可变
1.6声明字符串
print(“$str1$str2”)
print(str1+str2) //字符串的连接
1.7List的声明
var list
1 2 3 4 5 6 var list = ["test" ,20 ,true ];var list =<String >["test" ,"test1" ];list.length; list.add("add" );
创建一个固定长度的数组
第一个参数为长度,第二个参数为填充的内容,无法增加数据,无法修改长度
1 2 3 4 void main() { var list=List .filled(2 ,"1" ); print (list); }
1.8Map
第一种方式
1 2 3 4 5 6 7 var persion={ "name" :"Dart" , "age" :"8" }; print (persion); print (persion['name' ]); print (persion['age' ]);
第二种方式
1 2 3 4 5 6 var persion1=new Map ();persion1["name" ]="张三" ; persion1["age" ]="9" ; print (persion1); print (persion1['name' ]); print (persion1['age' ]);
1.9is判断类型 1 2 3 4 5 6 7 8 var str = '111' ;if (str is String ) { print ("str is String type" ); } else if (str is int ) { print ("str is int type" ); } else { print ("str is other type" ); }
1.10.运算符
+-*/ %取余 ~/取整
> , < , <= , >= , == , !=
&&并且 || 或者 !取反
+=,-=,*=,/=,%=,~/=
判断为空之后赋值(??=)
1 2 3 4 5 6 var a;var b = a ?? 10 ;print (b);
1.11.类型转换 1 2 3 String str='111' ;var myNum=int .parse(str);
其他类型转boolean
1 2 3 4 5 6 7 var mynum="" ; if (mynum.isEmpty){ print ("空" ); }else { print ("非空" ); }
a++
a—
自增自减运算
1.12.循环
for (int i=0;i<50;i++) while() do()while() 与java类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void main(){ List list = [{ "title" :"新闻1" , },{ "title" :"新闻2" , },{ "title" :"新闻3" , },{ "title" :"新闻4" , }]; for (var ele in list) { print (ele["title" ]); } }
1.13.List
常用属性
length 长度
reversed 翻转
isEmpty 是否为空
isNotEmpty 是否不为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 list.add("test" ); List newlist = List <String >.filled(2 , "1" );list.addAll(list1); List newlist = list.reversed.toList();List list = ["1" ,"2" ,"3" ];print (list.indexOf("1" ));list.remove("1" ); list.fillRange(1 ,2 ,'aaa' ); list.insert(1 ,"4" ); list.insertAll(1 ,["5" ,"6" ]); var desc = list.join('-' );var list = desc.split('-' );
1.14.Set
去除数组重复的内容
Set是没有顺序且不能重复的集合,所以不能通过所有去获取值
keys 获取所有的key值
values 获取所有的value值
isEmpty 判断是否为空
isNotEmpty 判读是否不为空
1.15.遍历 1.forEach 1 2 3 list.forEach((value){ print (value); });
1 2 3 4 5 6 7 Map person = { "name" :"tom" , "age" :19 }; person.forEach((key,value){ print (key.toString()+"-" +value.toString()); });
2.map 1 2 3 4 var newlist = list.map((value){ return value + "6" ; }); print (newlist.toList());
3.any 1 2 3 4 5 var res = list.any((value){ return value>5 ; }); print (res);
4.every 1 2 3 4 5 var res = list.every((value){ return value>5 ; }); print (res);
2.函数 2.1.定义
可以在main函数内或者main函数外
1 2 3 4 void printinfo(){ print ("test" ); } printinfo();
2.可选参数
此时的gender 和 age为可选参数
1 2 3 4 5 6 String test(String name,[String gender,int age]){ if (age != null ){ return "name:${name} ,gender:${gender} " ; } return "name:${name} ,gender:${gender} ,age:${age} " ; }
3.默认参数 1 2 3 4 5 6 String test(String name,[String gender="man" ,int age]){ if (age != null ){ return "name:${name} ,gender:${gender} " ; } return "name:${name} ,gender:${gender} ,age:${age} " ; }
4.命名参数 1 2 3 4 5 6 7 String test(String name,{String gender="man" ,int age}){ if (age != null ){ return "name:${name} ,gender:${gender} " ; } return "name:${name} ,gender:${gender} ,age:${age} " ; }
5.将函数当做参数传递 1 2 3 4 5 6 7 8 fn1(fn2){ fn2(); } fn2(){ print ("object" ); } fn1(fn2);
6.使用匿名函数传递参数 1 2 3 4 5 6 7 var fn = ()=>{ print ("mytest" ); }; fn3(fn){ fn(); } fn3(fn);
7.箭头函数 1 list.forEach((item)=>print (item.toString()));
8.自执行方法 1 2 3 ((value){ print (value); })(2 );
9.闭包 1 2 3 4 5 6 void test1(){ var a = 0 ; print (a); a++; } test1();
3.类 1.定义 1 2 3 4 5 6 7 8 9 10 11 class Person { String name = "zs" ; String email = "123@git.com" ; String address = "America" ; void getinfo(){ print ("${name} ${email} ${address} " ); } }
1 2 3 var p1 = new Person();print (p1.name);p1.getinfo();
2.构造函数 1 2 3 4 5 Person(String name,String email,String address){ this .name = name; this .email = email; this .address = address; }
3.简写的构造函数 1 Person(this .name,this .email,this .address);
4.命名构造函数
1 2 3 4 Person.now(){ print ("命名构造函数" ); } var p = new Person.now();
5.私有成员属性,加下划线 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person { String _name = "zs" ; String _email = "123@git.com" ; String _address = "America" ; Person(); String _getname(){ return this ._name; } void getinfo(){ print ("${this ._name} ${this ._email} ${this ._address} " ); } }
6.get set 1 2 3 4 5 6 7 8 9 10 11 12 class Rect { num height; num width; Rect(this .height,this .width); get area{ return height*width; } set areaheight(value){ this .height = value; } }
7.在实例化之前进行赋值操作 1 2 3 Rect():height=2 ,width=10 { }
8.静态属性和静态方法static
在属性和方法前加上static
非静态方法可以调用静态成员和非静态成员
静态方法无法访问非静态的属性和方法
9.对象操作符
?条件运算符
as 类型转换
is类型判断 Person is Object ====>true
..联级操作
1 2 3 4 var s;s="" ; s=new Person(); (s as Person).getinfo();
1 2 3 4 5 Person p = new Person(); p..name = "city" ..email = "123@m.com" ..getinfo();
4.继承 1.定义继承 1 2 3 4 5 6 7 class Animal {} class Cat extends Animal { }
2.实例化子类给父类传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Animal { String name; String kind; Animal(this .name,this .kind); getinfo(){ print ("${name} -${kind} " ); } } class Cat extends Animal { Cat(String name,String kind):super (name,kind); }
在覆写父类的方法时可以加上@override也可以不写
super.父类方法名 调用父类的方法
2.抽象类
抽象并且定义标准
父类定义一个方法不去实现,子类有不同的实现 —》 多态
1 2 3 4 5 6 7 8 9 10 abstract class Wolf { void run(); } class Dog extends Wolf { @override void run(){ print ("run" ); } }
3.一个类实现多个接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 abstract class A { void run(); } abstract class B { void walk(); } class C implements A ,B { @override void run() { } @override void walk() { } }
4.mixins 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mixin A{ void run(){ print ("A" ); } } mixin B{ void walk(){ print ("B" ); } } class C with A ,B {}
1.作为mixins的类只能继承自Object,不能继承其他类
2.作为mixins的类不能有构造函数
3.一个类可以mixins多个mixins的类
4.minxins不是继承,也不是接口,而是一种特性
C c = new C()
print(C is A)
print(c is B)
print(c is C)
5.泛型 1.泛型方法 1 2 3 4 5 print (getData<num >(12 ));T getData<T>(T value){ return value; }
2.泛型类 1 2 3 4 5 6 class MyList <T > { List list = <T>[]; void add(value){ list.add(value); } }
3.泛型接口
文件缓存,内存缓存
1.getBykey 和 setBykey
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 abstract class Cache <T > { getByKey(String key); setByKey(String key,T value); } class FileCache <T > implements Cache { @override getByKey(String key) { return null ; } @override setByKey(String key, value) { print (key +"=" + value); } } class MemoeryCache <T > implements Cache { @override getByKey(String key) { return null ; } @override setByKey(String key, value) { print (key +"=" + value); } }
3.内置库dart:io
可以使用async 和 await
async让方法变成异步
await等待异步方法的完成
4.第三方库
pub get
The official repository for Dart and Flutter packages. (pub.dev)
1 2 3 import '' show get ; import '' hiden get ; import '' deferred ;
6. 2.13新特性 1.可空类型 1 2 3 4 5 int a=12 ; a = null ; int? a=12 ; a=null ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 String test(url){ if (url !=null ){ return 'test' ; } return null ; } String? test(url){ if (url !=null ){ return 'test' ; } return null ; }
2.类型断言 1 2 3 4 void test(String? url){ print (url!.length); }
1 2 3 4 5 6 7 8 void test(String? url){ try { print (url!.length); }catch (error){ print ("url为空" ); } }
3.late关键字
late关键字用来延迟初始化
1 2 3 4 5 6 7 8 9 class person { late String name; late String age; void setname(String name,String age){ this .name = name; this .age = age; } }
4.require 用来标记为必填参数 1 2 3 void PrintTest({required String name,required String age}){}
7.性能优化 1.常量
const PI = 3.14 –>定义常量
const final
const 声明的常量是在编译时确定的
final 声明的常量允许声明后再赋值,赋值后不可改变s
2.identical 1 2 3 4 var a = new Object ();var b = new Object ();print (identical(a, b));
const 在多个地方定义相同的对象时,内存只保留了一个对象
1 2 3 4 const a = Object ();const b = Object ();print (identical(a, b));
3.常量构造函数
常量构造函数以const开头
const 构造函数必须用于成员变量都是final的类
如果实例化不加const,即使调用的是常量构造函数 ,实例化的成员也不是常量实例对象
1 2 3 4 5 6 7 8 9 class person { final String name; final String age; const person({required this .name,required this .age}); } const a = const person(name:"test" ,age:"12" );const b = const person(name:"test" ,age:"12" );print (identical(a, b));
2.Flutter基础
配置JDK,下载Android studio
1.配置Flutter 1.下载SDK https://docs.flutter.dev/get-started/install/windows/mobile
2.配置镜像
PUB_HOSTED_URL:https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL:https://storage.flutter-io.cn
3.配置环境变量
sdk的bin目录
4.报错说需要同意证书
安装安卓命令行工具 cmdline-tools
flutter doctor –android-licenses
2.flutter项目文件
创建flutter项目
flutter create rctool
lib –> 所有的代码将会写在lib目录下
pubspec.yaml —>配置文件
3.入口main.dart 1 2 3 4 5 6 7 8 void main(){ runApp(const Center( child: Text("哈哈哈" ,textDirection: TextDirection.ltr,style: TextStyle( color: Color.fromRGBO(35 , 67 , 188 , 1 ), fontSize: 40 ),), )); }
3.Flutter组件
MaterialApp和Scaffold两个组件装饰APP
1.MaterialApp 1 2 3 4 5 6 7 8 9 10 11 runApp(MaterialApp( home: Scaffold( appBar: AppBar(title: Text("hello flutter" )), body: const Center( child: Text("哈哈哈" ,textDirection: TextDirection.ltr,style: TextStyle( color: Color.fromRGBO(35 , 67 , 188 , 1 ), fontSize: 40 )) )) )); }
statelessWidget 无状态组件,状态不可变的Widget
statefulWidget 状态组件,状态可能在生命周期发生改变
1 2 3 4 5 6 7 8 9 10 11 12 13 class myApp extends StatelessWidget { const myApp({Key? key}) : super (key: key); @override Widget build(BuildContext context) { return const Center( child: Text("哈哈哈" ,textDirection: TextDirection.ltr,style: TextStyle( color: Color.fromRGBO(35 , 67 , 188 , 1 ), fontSize: 40 )) ); } }
2.Container容器组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 alignment(这个属性是针对容器中的child的对其方式) topCenter:顶部居中对齐 topLeft:顶部左对齐 topRight:顶部右对齐 center:水平垂直居中对齐 centerLeft:垂直居中水平居左对齐 centerRight:垂直居中水平居右对齐 bottomCenter底部居中对齐 bottomLeft:底部居左对齐 bottomRight:底部居右对齐 decoration(容器的修饰器,用于背景或者border) 如果在decoration中用了color,就把容器中设置的color去掉,不要重复设置color,设置的边框样式是让四条边框的宽度为8px,颜色为黑 色 margin(margin属性是表示Container与外部其他组件的距离) padding(指Container边缘与Child之间的距离) padding: EdgeInsets.all(20.0), transform(可以根据自己的需要调整旋转的角度) transform: Matrix4.rotationZ(0.2) width宽度 height高度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class myApp extends StatelessWidget { const myApp({super .key}); @override Widget build(BuildContext context) { return Center( child: Container( alignment: Alignment.center, width: 100 , height: 100 , decoration: BoxDecoration( color: Colors.red, border: Border.all( color: Colors.blue, width: 2 ), borderRadius: BorderRadius.circular(50 ), boxShadow: const [ BoxShadow(color:Colors.black, blurRadius: 10.0 ) ], gradient: const LinearGradient(colors: [Colors.amberAccent,Colors.red]) ), child: const Text("data" ,style: TextStyle(color: Colors.amberAccent),), ) ); } }
margin
1 margin: const EdgeInsets.fromLTRB(left, top, right, bottom)
transform 移动
1 2 3 transform: Matrix4.translationValues(x, y, z), transform: Matrix4.rotationX(radians), transform: Matrix4.skewX(alpha),
3.Text组件
style
文字样式(颜色、粗细)
textAlign
文字对齐样式
overflow
文字字数超出后样式
textScaleFactor
文字大小
maxLines
最大行数
4.Image组件 1.加载远程图片
1 2 3 child: Image.asset("assert/a8f6066ff11c400a10419beba3dd2a61.jpg" , fit: BoxFit.fill, ),
fit 可以控制图片的填充形式,宽度填满等
实现圆角图片
1 2 3 4 5 6 7 decoration: BoxDecoration( borderRadius: BorderRadius.circular(150 ), image: const DecorationImage( image: NetworkImage("https://pss.bdstatic.com/static/superman/img/logo/bd_logo1-66368c33f8.png" ), fit:BoxFit.cover ), ),
ClipOval实现圆角图片
1 2 3 4 5 6 7 return ClipOval( child: Image.network("https://pss.bdstatic.com/static/superman/img/logo/bd_logo1-66368c33f8.png", width: 150, height: 150, fit: BoxFit.cover, ), );
2.加载本地图片
pubspec.yaml
1 2 3 4 5 6 7 8 9 10 11 12 flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: assets: - images/a.jpg - images/2.0x/a.jpg - images/3.0x/a.jpg
5.Icon图标 1 Icon(Icons.home,size: 40 ,color: Colors.blue,)
1.pubspec.yaml 1 2 3 4 fonts: - family: richuicon //自定义字体名 fonts: - asset: fonts/iconfont.ttf
2.自定义图标 1 2 3 4 5 6 7 8 9 10 11 12 13 class richuicon { static const IconData check = IconData( 0xe645 , fontFamily: "richuicon" , matchTextDirection: true ); static const IconData close = IconData( 0xe646 , fontFamily: "richuicon" , matchTextDirection: true ); }
3.使用 1 Icon(richuicon.check,size: 40 ,color: Colors.amberAccent,)
6.ListView 1.静态
可滑动
1 2 3 4 5 ListView( children: const <Widget>[myApp(),myButton(),myImage(),Icon(Icons.home,size: 40 ,color: Colors.blue,), Icon(richuicon.check,size: 40 ,color: Colors.amberAccent,) ], )
scrollDirection Axis.horizationtal水平列表 Axis.vertical垂直列表
2.动态展示数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class mylist extends StatelessWidget { List <Widget> initdata(){ List <Widget> list = []; for (var i=0 ;i<10 ;i++){ list.add( ListTile( title: Text("data$i " ), )); } return list; } const mylist({super .key}); @override Widget build(BuildContext context) { return ListView( children: initdata(), ); } }
使用ListViewBuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class myview extends StatelessWidget { List <String > list = []; myview({super .key}){ for (var i=0 ;i<10 ;i++){ list.add("data$i " ); } } @override Widget build(BuildContext context) { return ListView.builder( itemCount: list.length, itemBuilder:(context,index){ return ListTile( title: Text("data:${list[index]} " ), ); }, ); } }
7.网格组件Gridview
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class mygrid extends StatelessWidget { const mygrid({super .key}); @override Widget build(BuildContext context) { return GridView.count(crossAxisCount: 3 , children: const [ Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter), Icon(Icons.bike_scooter) ], ); } }
1、GridView.count(@required int crossAxisCount)
GridView.count的使用,需要传的是int类型的参数,也就是创建固定数量的item,crossAxisCount表示横轴item的个数(系统默认是横轴);
2、GridView.extent(@required double maxCrossAxisExtent)
GridView.extent的使用,需要传的是double类型的参数,也就是创建横轴上最大可容纳的item,maxCrossAxisExtent表示横轴item的最大宽度;
3、GridView.builder(@required this.gridDelegate,@required IndexedWidgetBuilder itemBuilder)
适用于widget数量多的时候,通过GridView.builder的使用,动态的创建widget,itemBuilder表示子widget构造器;
4、GridView.custom(@required this.gridDelegate,@required this.childrenDelegate)
GridView.custom的使用,通过两个代理方法gridDelegate和childrenDelegate来创建Gridview,其中gridDelegate是进行布局的代理,控制每列或每行的子widget的数量,以及上下左右间距和宽高比例;childrenDelegate的代理方法有两种实现方式,在使用的时候需要注意二者的区别。
属性
说明
scrollDirection
滚动方向
reverse
组件反向排序
controller
滚动控制(滚动监听)
primary
如果内容不足,则用户无法滚动 而如果[primary]为true,它们总是可以尝试滚动。
physics
滑动类型设置 AlwaysScrollableScrollPhysics 总是可以滑动 NeverScrollableScrollPhysics 禁止滚动 BouncingScrollPhysics 内容超过一屏 上拉有回弹效果 ClampingScrollPhysics 包裹内容 不会有回弹
shrinkWrap
默认false 内容适配
padding
内边距
crossAxisCount
列 数量
mainAxisSpacing
垂直子 Widget 之间间距
crossAxisSpacing
水平子 Widget 之间间距
childAspectRatio
子 Widget 宽高比例
addAutomaticKeepAlives
默认true
addRepaintBoundaries
默认true
addSemanticIndexes
默认true
cacheExtent
设置预加载的区域
children
子元素
semanticChildCount
将提供语义信息的子代数量
dragStartBehavior
GridView.builder独有属性
gridDelegate
一个控制 GridView 中子项布局的委托。
itemBuilder
遍历数返回Widget
itemCount
子控件数量
GridView.builder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 lass mybuild extends StatelessWidget{ Widget initGridData(context,index){ return Container( decoration: const BoxDecoration( color: Colors.blueAccent ), child: Column( children: [ Image.asset("images/a.jpg" ), const Text("hahaha" ) ], ), ); } @override Widget build(BuildContext context) { return GridView.builder( padding: const EdgeInsets.fromLTRB(0 , 10 , 0 , 0 ), itemCount: 10 , gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2 , crossAxisSpacing: 10 , mainAxisSpacing: 10 , childAspectRatio: 1.2 ), itemBuilder: initGridData); } }
8.Padding组件
padding padding值,EdgeInsets设置填充的值
child 子组件
9.Switch组件 Switch 是一个用于表示布尔值(开/关)的开关组件,通常用于切换某种状态。它是一个 Material Design 风格的组件,提供了丰富的自定义选项。
1. 基本用法 Switch 组件的最基本用法是通过 value 属性设置当前状态,并通过 onChanged 回调处理状态变化。
1 2 3 4 5 6 7 8 Switch( value: _isSwitched, onChanged: (bool newValue) { setState(() { _isSwitched = newValue; }); }, )
value
:布尔值,表示开关的当前状态(true 表示开启,false 表示关闭)。onChanged
:当用户切换开关时触发的回调函数,参数是新的布尔值。
2. 自定义样式 Switch 提供了多个属性,可以自定义其外观:
2.1 颜色
activeColor
:开关处于开启状态时的颜色。inactiveThumbColor
:开关处于关闭状态时滑块的颜色。inactiveTrackColor
:开关处于关闭状态时轨道的颜色。
1 2 3 4 5 6 7 8 9 Switch( value: _isSwitched, onChanged: (bool value) { setState(() => _isSwitched = value); }, activeColor: Colors.blue, inactiveThumbColor: Colors.white, inactiveTrackColor: Colors.grey, )
2.2 大小
activeThumbImage
和 inactiveThumbImage
:可以使用自定义图像作为滑块。materialTapTargetSize
:设置开关的触摸目标大小。
1 2 3 4 5 6 7 8 9 10 Switch( value: _isSwitched, onChanged: (bool value) { setState(() => _isSwitched = value); }, activeColor: Colors.blue, inactiveThumbColor: Colors.white, inactiveTrackColor: Colors.grey, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, )
4.线性布局 1.Row组件
宽度自适应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void main(){ runApp(MaterialApp( home: Scaffold( appBar: AppBar(title: const Text("hello flutter" )), body: const SizedBox( height: 400 , width: 400 , child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children:[ IconContainer(),IconContainer(),IconContainer()], ), ) ) ) ); } class IconContainer extends StatelessWidget { const IconContainer({super .key}); @override Widget build(BuildContext context) { return Container( height: 100 , width: 100 , alignment: Alignment.center, decoration:const BoxDecoration( color:Colors.red ), child:const Icon(Icons.add,color: Colors.blueAccent,size: 32 ,), ); } }
2.Column布局
高度自适应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void main(){ runApp(MaterialApp( home: Scaffold( appBar: AppBar(title: const Text("hello flutter" )), body: const SizedBox( height: 400 , width: 400 , child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children:[ IconContainer(),IconContainer(),IconContainer()], ), ) ) ) ); } class IconContainer extends StatelessWidget { const IconContainer({super .key}); @override Widget build(BuildContext context) { return Container( height: 100 , width: 100 , alignment: Alignment.center, decoration:const BoxDecoration( color:Colors.red ), child:const Icon(Icons.add,color: Colors.blueAccent,size: 32 ,), ); } }
3.弹性布局
Expanded
flex 设置占的份数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void main(){ runApp(MaterialApp( home: Scaffold( appBar: AppBar(title: const Text("hello flutter" )), body: const Flex( direction: Axis.horizontal, children: [ Expanded( flex:1 , child: IconContainer() ), Expanded( flex:2 , child: IconContainer() ), ], ) ) ) ); }
4.Stack层叠组件
以堆的方式放入children子组件,所有的组件都堆在一起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void main(){ runApp(MaterialApp( home: Scaffold( appBar: AppBar(title: const Text("hello flutter" )), body: Stack( children: [ Container( height: 400 , width: 500 , color: Colors.red, ), const Text("data" ,textAlign: TextAlign.center,) ], ) ) ) ); }
Positioned
top
bottom
left
right
height
width (宽度和高度必须为固定值,不能为double.infinity)
定位类型css的position
5.Align
可以控制子组件的位置
1 2 3 4 5 6 7 8 9 10 11 12 void main(){ runApp(MaterialApp( home: Scaffold( appBar: AppBar(title: const Text("hello flutter" )), body: const Align( alignment: Alignment.center, child: Text("data" ), ) ) ) ); }
6.AspectRatio
aspectRatio 宽高比
1 2 3 4 5 6 7 8 9 10 11 12 13 class myaspect extends StatelessWidget { const myaspect({super .key}); @override Widget build(BuildContext context) { return AspectRatio( aspectRatio: 3 /1 , child: Container( color: Colors.red, ), ); } }
7.Card 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class mycard extends StatelessWidget { const mycard({super .key}); @override Widget build(BuildContext context) { return Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10 ) ), elevation: 10 , margin:const EdgeInsets.fromLTRB(0 , 10 , 0 , 0 ) , child: const Column( children: [Text("data" )], ) ); } }
8.CircleAvatar
通过CircleAvatar 实现圆形图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class mycard extends StatelessWidget { const mycard({super .key}); @override Widget build(BuildContext context) { return Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10 ) ), elevation: 10 , margin:const EdgeInsets.fromLTRB(0 , 10 , 0 , 0 ) , child: const Column( children: [Text("data" ), CircleAvatar( radius: 10 , backgroundImage: AssetImage("images/a.jpg" ), ) ], ) ); } }
9.ElevatedButton,TextBtton,IconButton 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class myButton extends StatelessWidget { const myButton({super .key}); @override Widget build(BuildContext context) { return SizedBox( child: ElevatedButton.icon(onPressed: (){}, icon:const Icon(Icons.access_alarm),label: const Text("提醒" ,style: TextStyle(color: Colors.red,))), ); } }
ButtonStyle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class myButton1 extends StatelessWidget { const myButton1({super .key}); @override Widget build(BuildContext context) { return SizedBox( child: SizedBox( child: ElevatedButton.icon( style: ButtonStyle( backgroundColor: WidgetStateProperty.all(Colors.amberAccent), foregroundColor: const WidgetStatePropertyAll(Colors.blueAccent) ), onPressed: (){}, icon:const Icon(Icons.access_alarm), label: const Text("提醒" ,style: TextStyle(color: Colors.red,fontSize: 10 ))), ), ); } }
10.Wrap组件
当x轴无法显示时可以显示到y轴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 void main(){ runApp(MaterialApp( home: Scaffold( body: ListView( padding: const EdgeInsets.all(10 ), children:[ Layout("热搜" ),Wrap( spacing: 10 , alignment: WrapAlignment.spaceAround, runSpacing: 10 , children: [ElevatedButton(onPressed:(){}, child: const Text("1" ) ), ElevatedButton(onPressed:(){}, child: const Text("2" ) ), ElevatedButton(onPressed:(){}, child: const Text("3" ) ), ElevatedButton(onPressed:(){}, child: const Text("4" ) ), ElevatedButton(onPressed:(){}, child: const Text("5" ) ), ElevatedButton(onPressed:(){}, child: const Text("6" ) ), ElevatedButton(onPressed:(){}, child: const Text("7" ) ), ElevatedButton(onPressed:(){}, child: const Text("8" ) ), ElevatedButton(onPressed:(){}, child: const Text("9" ) ), ], ),const SizedBox(height: 10 ,), Layout("历史记录" ), const Divider(), const Column( children: [ListTile(title: Text("5" ), ),Divider(), ListTile(title: Text("6" )), Divider() ], ) ], ) ) ) ); } class Layout extends StatelessWidget { var text; Layout(this .text,{super .key}); @override Widget build(BuildContext context) { return Row( children: [ Text(text,style: Theme.of(context).textTheme.titleLarge,) ], ); } }
实现点击按钮++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { int _num = 0 ; @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text("$_num " ), const SizedBox(height: 10 ,), ElevatedButton(onPressed: (){ setState(() { _num ++; }); }, child:const Text("++" )) ], ), ); } }
添加一个浮动按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 void main(){ runApp( MaterialApp( title: "richu" , home: Scaffold( body:HomeState(), floatingActionButton: FloatingActionButton(onPressed: (){ },child: Text("click me" ),), ), ) ); }
实现动态列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void main(){ runApp( const MaterialApp( title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { final List <String > _list = []; int num = 1 ; @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton(onPressed: (){ setState(() { _list.add("data$num " ); num ++; }); },child: const Text("+" ),), appBar: AppBar(title:const Text("richu" )), body: ListView( children:_list.map((v){ return ListTile( title: Text(v), ); }).toList() ), ); } }
1.BottomNavigationBar
自定义底部导航栏
items 配置底部菜单项
Ontab 点击事件
curretIndex 索引
Iconsize 底部图标的大小
fixedColor 选中的颜色
type:BottomNavigationBarType.fixed 如果菜单超过三个则需要加上这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void main(){ runApp( const MaterialApp( title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { int currentId = 0 ; @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: BottomNavigationBar(onTap: (index){ setState(() { currentId = index; }); },currentIndex: currentId,items: const [ BottomNavigationBarItem(icon: Icon(Icons.home),label: "首页" ), BottomNavigationBarItem(icon: Icon(Icons.settings),label: "设置" ), BottomNavigationBarItem(icon: Icon(Icons.supervised_user_circle),label: "用户" )]), appBar: AppBar(title:const Text("richu" )), body: const Text("data" ) ); } }
2.实现页面切换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import 'package:flutter/material.dart' ;import './tabs/user.dart' ;import './tabs/home.dart' ;import './tabs/setting.dart' ;void main(){ runApp( const MaterialApp( title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { int currentId = 0 ; final List <Widget> _list = const [ MyHomePage(), SettingPage(), UserPage(), ]; @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: BottomNavigationBar(onTap: (index){ setState(() { currentId = index; }); },currentIndex: currentId,items: const [ BottomNavigationBarItem(icon: Icon(Icons.home),label: "首页" ), BottomNavigationBarItem(icon: Icon(Icons.settings),label: "设置" ), BottomNavigationBarItem(icon: Icon(Icons.supervised_user_circle),label: "用户" )]), appBar: AppBar(title:const Icon(Icons.arrow_back)), body: _list[currentId] ); } }
shape:const CircleBorder()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 void main(){ runApp( const MaterialApp( title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { int currentId = 0 ; final List <Widget> _list = const [ MyHomePage(), SettingPage(), UserPage(), ]; @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: BottomNavigationBar(onTap: (index){ setState(() { currentId = index; }); },currentIndex: currentId,fixedColor: Colors.blue,type: BottomNavigationBarType.fixed,items: const [ BottomNavigationBarItem(icon: Icon(Icons.home),label: "首页" ), BottomNavigationBarItem(icon: Icon(Icons.settings),label: "设置" ), BottomNavigationBarItem(icon: Icon(Icons.supervised_user_circle),label: "用户" ), ]), appBar: AppBar(title:const Icon(Icons.arrow_back)), floatingActionButton: Container( height: 60 , width: 60 , decoration:BoxDecoration( color: Colors.black12, borderRadius: BorderRadius.circular(30 ) ), child: FloatingActionButton(shape:const CircleBorder(),onPressed: (){ setState(() { currentId = 1 ; }); },child: const Icon(Icons.add),),), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, body: _list[currentId] ); } }
4.Drawer侧边栏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 void main(){ runApp( const MaterialApp( title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { int currentId = 0 ; final List <Widget> _list = const [ MyHomePage(), SettingPage(), UserPage(), ]; @override Widget build(BuildContext context) { return Scaffold( drawer: const Drawer( child: Column( children: [ DrawerHeader(child: ListTile(leading: Icon(Icons.home),title: Text("信息" ), )), Divider(), ListTile( leading: CircleAvatar(child:Icon(Icons.supervised_user_circle)), title: Text("用户中心" ), ),Divider(), ListTile( leading: CircleAvatar(child:Icon(Icons.delete)), title: Text("删除" ), ),Divider() ], ), ), bottomNavigationBar: BottomNavigationBar(onTap: (index){ setState(() { currentId = index; }); },currentIndex: currentId,fixedColor: Colors.blue,type: BottomNavigationBarType.fixed,items: const [ BottomNavigationBarItem(icon: Icon(Icons.home),label: "首页" ), BottomNavigationBarItem(icon: Icon(Icons.settings),label: "设置" ), BottomNavigationBarItem(icon: Icon(Icons.supervised_user_circle),label: "用户" ), ]), appBar: AppBar(title:const Icon(Icons.arrow_back)), body: _list[currentId] ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class _HomeState extends State <HomeState > { int currentId = 0 ; final List <Widget> _list = const [ MyHomePage(), SettingPage(), UserPage(), ]; @override Widget build(BuildContext context) { return Scaffold( drawer: const Drawer( child: Column( children: [ UserAccountsDrawerHeader(accountName: Text("日初" ), accountEmail: Text("xxx@163.com" ),currentAccountPicture:CircleAvatar(backgroundImage: AssetImage("images/a.jpg" ))), Divider(), ListTile( leading: CircleAvatar(child:Icon(Icons.supervised_user_circle)), title: Text("用户中心" ), ),Divider(), ListTile( leading: CircleAvatar(child:Icon(Icons.delete)), title: Text("删除" ), ),Divider() ], ), ), bottomNavigationBar: BottomNavigationBar(onTap: (index){ setState(() { currentId = index; }); },currentIndex: currentId,fixedColor: Colors.blue,type: BottomNavigationBarType.fixed,items: const [ BottomNavigationBarItem(icon: Icon(Icons.home),label: "首页" ), BottomNavigationBarItem(icon: Icon(Icons.settings),label: "设置" ), BottomNavigationBarItem(icon: Icon(Icons.supervised_user_circle),label: "用户" ), ]), appBar: AppBar(title:const Icon(Icons.arrow_back)), body: _list[currentId] ); } }
6.Bar 1.AppBar
debugShowCheckedModeBanner: false 关闭debug图标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void main(){ runApp( const MaterialApp( debugShowCheckedModeBanner: false , title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton(onPressed: (){}, icon: const Icon(Icons.menu)), actions: [ IconButton(onPressed: (){}, icon: const Icon(Icons.add)) ], ), body:const Text("data" ) ); } }
2.TabBar
tabbar和tabbarView都要配置Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super .initState(); _tabController = TabController(length: 3 , vsync: this ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton(onPressed: (){}, icon: const Icon(Icons.menu)), actions: [ IconButton(onPressed: (){}, icon: const Icon(Icons.add)) ], bottom: TabBar(controller:_tabController ,tabs:const [ Tab(child: Text("推荐" )),Tab(child: Text("热门" )),Tab(child: Text("视频" ),) ]), ), body:TabBarView(controller: _tabController ,children:const [ListTile( title: Text("我的推荐" ), ), ListTile( title: Text("我的热门" ), ), ListTile( title: Text("我的视频" ), ) ]) ); } }
3.AppBar属性
字段
属性
描述
key
Key
当组件在组件树中移动时使用Key可以保持组件之前状态
leading
Widget
通常情况下返回一个返回键(IconButton)
leadingWidth
double
左侧leading的宽度,默认56
automaticallyImplyLeading
bool
和leading配合使用,如果为true并且leading为空的情况下,会自动配置返回键
title
Widget
导航栏的标题
centerTitle
bool
标题是否居中,不同操作系统默认显示位置不一样
actions
List
一个Widget列表
bottom
PreferredSizeWidget
出现在导航栏底部的控件
elevation
double
控制导航栏下方阴影的大小
shadowColor
Color
控制导航栏下方阴影的颜色
shape
ShapeBorder
导航栏的形状以及阴影
backgroundColor
Color
导航栏的背景颜色
foregroundColor(只有当14属性设置为flase的时候,该属性才会生效) )
Color
导航栏中文本和图标的颜色
backwardsCompatibility
bool
与foregroundColor配合使用
iconTheme
IconThemeData
导航栏图标的颜色、透明度、大小的配置
actionsIconTheme
IconThemeData
导航栏右侧图标的颜色、透明度、大小的配置
textTheme
TextTheme
导航栏文本的排版样式
primary
bool
导航栏是否显示在屏幕顶部
excludeHeaderSemantics
bool
标题是否应该用 [Semantics] 包裹,默认false
titleSpacing
double
title内容的间距
toolbarOpacity
double
导航栏的透明度
toolbarHeight
double
导航栏的高度,默认kToolbarHeight
toolbarTextStyle
TextStyle
导航栏图标的颜色
titleTextStyle
TextStyle
导航栏标题的默认颜色
flexibleSpace
Widget
堆叠在工具栏和选项卡栏的后面
systemOverlayStyle
SystemUiOverlayStyle
叠加层的样式
brightness
Brightness
导航栏的亮度,改属性已废弃,用systemOverlayStyle代替
bottomOpacity
double
导航栏底部的透明度
3.PreferredSize配置appBar的高度 1 2 3 4 5 6 7 appBar: PreferredSize(preferredSize: const Size.fromHeight(50 ), child: TabBar( controller:_tabController , indicatorColor: Colors.red, labelColor: Colors.black87, tabs:const [ Tab(child: Text("推荐" )),Tab(child: Text("热门" )),Tab(child: Text("视频" ),) ]),),
4.自定义缓存组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class KeepAliveWrapper extends StatefulWidget { final Widget? child; final bool keepAlive; const KeepAliveWrapper({Key? key,required this .child,this .keepAlive=true }):super (key: key); @override State<KeepAliveWrapper> createState() => _KeepAliveWrapper(); } class _KeepAliveWrapper extends State <KeepAliveWrapper > with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { return widget.child!; } @override bool get wantKeepAlive => widget.keepAlive; }
实现页面的缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import 'package:flutter/material.dart' ;class KeepAliveWrapper extends StatefulWidget { final Widget? child; final bool keepAlive; const KeepAliveWrapper({Key? key,required this .child,this .keepAlive=true }):super (key: key); @override State<KeepAliveWrapper> createState() => _KeepAliveWrapper(); } class _KeepAliveWrapper extends State <KeepAliveWrapper > with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { return widget.child!; } @override bool get wantKeepAlive => widget.keepAlive; } void main(){ runApp( const MaterialApp( debugShowCheckedModeBanner: false , title: "richu" , home: HomeState() )); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=> _HomeState(); } class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super .initState(); _tabController = TabController(length: 3 , vsync: this ); } @override Widget build(BuildContext context) { return Scaffold( appBar: PreferredSize(preferredSize: const Size.fromHeight(50 ), child: TabBar( controller:_tabController , indicatorColor: Colors.red, labelColor: Colors.black87, tabs:const [ Tab(child: Text("推荐" )),Tab(child: Text("热门" )),Tab(child: Text("视频" ),) ]),), backgroundColor: Colors.white, body:TabBarView(controller: _tabController ,children:[KeepAliveWrapper(child: ListView(children: const [ListTile( title: Text("我的推荐" ), )],),), const ListTile( title: Text("我的热门" ), ), const ListTile( title: Text("我的视频" ), ) ]) ); } }
5.tabController实现事件监听 1 2 3 4 5 6 7 8 9 10 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super .initState(); _tabController = TabController(length: 3 , vsync: this ); _tabController.addListener((){ print (_tabController.index); }); }
解决触发两次的问题
1 2 3 4 5 6 7 8 9 10 11 12 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super .initState(); _tabController = TabController(length: 3 , vsync: this ); _tabController.addListener((){ if (_tabController.index == _tabController.animation!.value){ print (_tabController.index); } }); }
7.路由 1.Navigator.of(context).push()
可以通过Navigator.push()跳转到另一个页面
Navigator.pop()返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class UserPage extends StatefulWidget { const UserPage({super .key}); @override State<UserPage> createState() => _UserPage(); } class _UserPage extends State <UserPage > { @override Widget build(BuildContext context) { return Center( child: Column( children: [ ElevatedButton(onPressed: (){ Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext builde){ return const MyHomePage(); }) ); }, child: const Text("搜索" )) ], ) ); } }
2.基本路由
可以使用widget.属性获取父类的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class SettingPage extends StatefulWidget { final String title; const SettingPage({super .key,this .title = "" }); @override State<SettingPage> createState() => _SettingPage(); } class _SettingPage extends State <SettingPage > { @override Widget build(BuildContext context) { return Center( child: Text(widget.title), ); } }
此时传递参数,在构造函数传递title即可
1 2 3 4 5 6 7 ElevatedButton(onPressed: (){ Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext builde){ return const SettingPage(title: "我是设置" ); }) ); }, child: const Text("设置" ))],
3.命名路由 1 2 3 4 ElevatedButton(onPressed: (){ Navigator.pushNamed(context, "/setting" ); }, child: const Text("设置" )) ],
4.命名路由传值 1.配置Map routes 1 2 3 4 5 Map routes = { "/" : (context) => const MyHomePage(), "/news" : (context) => const NewsPage(), "/setting" : (context,arguments) => SettingPage(arguments: arguments), };
2.配置onGenerateRoute 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 onGenerateRoute: (RouteSettings settings){ final String? name = settings.name; final Function? builder = routes[name]; if (builder != null ){ if (settings.arguments != null ){ final Route route = MaterialPageRoute( builder: (context) => builder(context,arguments:settings.arguments) ); return route; }else { final Route route = MaterialPageRoute( builder: (context) => builder(context) ); return route; } }
3.传递参数arguments 1 2 3 4 5 6 ElevatedButton(onPressed: (){ Navigator.pushNamed(context, "/news" ,arguments: { "title" :"我是新闻" , "aid" :20 }); }, child: const Text("新闻" ))
4.flutter返回上一级路由
Navigator.of(context).pop();
5.替换路由 1 2 3 4 5 return ListView( children: [Text(widget.arguments["title" ]),ElevatedButton(onPressed: (){ Navigator.of(context).pushReplacementNamed("/" ); }, child: const Text("注册" ))], );
6.返回根目录 1 Navigator.of(context).pushAndRemoveUntil(newRoute, predicate);
7.使用IOS风格的路由跳转 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import 'package:flutter/cupertino.dart' ;onGenerateRoute: (RouteSettings settings){ final String? name = settings.name; final Function? builder = routes[name]; if (builder != null ){ if (settings.arguments != null ){ final Route route = CupertinoPageRoute( builder: (context) => builder(context,arguments:settings.arguments) ); return route; }else { final Route route = CupertinoPageRoute( builder: (context) => builder(context) ); return route; } } return null ; },
8.Dialog 1.AlertDialog 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class NewsPage extends StatefulWidget { final Map arguments; const NewsPage({super .key,required this .arguments}); @override State<NewsPage> createState() => _NewsPage(); } class _NewsPage extends State <NewsPage > { @override Widget build(BuildContext context) { void _alertDialog(){ showDialog(context: context, builder: (context){ return AlertDialog( title: const Text("提示信息" ), content: const Text("确定?" ), actions: [ TextButton(onPressed: (){ Navigator.of(context).pop("ok" ); }, child: const Text("确定" )), TextButton(onPressed: (){ Navigator.of(context).pop("none" ); }, child: const Text("取消" )) ], ); }); } return ListView( children: [Text(widget.arguments["title" ]),ElevatedButton(onPressed: (){ Navigator.of(context).pushReplacementNamed("/" ); }, child: const Text("注册" )),ElevatedButton(onPressed: (){ _alertDialog(); }, child: const Text("取消" ))], ); } }
2.异步获取消息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Widget build(BuildContext context) { void _alertDialog() async { var res = await showDialog(context: context, builder: (context){ return AlertDialog( title: const Text("提示信息" ), content: const Text("确定?" ), actions: [ TextButton(onPressed: (){ Navigator.of(context).pop("ok" ); }, child: const Text("确定" )), TextButton(onPressed: (){ Navigator.of(context).pop("none" ); }, child: const Text("取消" )) ], ); }); print (res); }
3.SimpleDialog 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void _simpleDialog()async { var res = await showDialog(context: context, builder: (context){ return SimpleDialog( title: const Text("提示信息" ), children: [ SimpleDialogOption( onPressed: (){ Navigator.pop(context,"1" ); }, child: const Text("1" ), ), SimpleDialogOption( onPressed: (){ Navigator.pop(context,"2" ); }, child: const Text("2" ), ), SimpleDialogOption( onPressed: (){ Navigator.pop(context,"3" ); }, child: const Text("3" ), ), ], ); }); print (res); }
4.showModalBottomSheet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void _modelDialog()async { var res = await showModalBottomSheet(context: context, builder: (context){ return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ ListTile( title: const Text("test1" ), onTap: (){ Navigator.pop(context,"test1" ); }, ), ListTile( title: const Text("test2" ), onTap: (){ Navigator.pop(context,"test2" ); }, ), ListTile( title: const Text("test3" ), onTap: (){ Navigator.pop(context,"test3" ); }, ) ], ); }); print (res); }
5.showToast
添加配置
1 2 3 4 dependencies: flutter: sdk: flutter fluttertoast: ^8.0.9
1 2 3 4 5 6 7 8 9 Fluttertoast.showToast( msg: "This is Center Short Toast" , toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, timeInSecForIosWeb: 1 , backgroundColor: Colors.red, textColor: Colors.white, fontSize: 16.0 );
6.自定义dialog
自定义dialog,并且点击按钮关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Mydialog extends Dialog { const Mydialog({super .key}); @override Widget build(BuildContext context){ return Material( child: Center( child: Container( height: 300 , width: 300 , color: Colors.white, child: Column( children: [ const SizedBox(height: 20 ,), Stack( children: [ const Align(alignment: Alignment.centerLeft,child: Text("提示" ),), Align(alignment: Alignment.centerRight,child: InkWell(child:const Icon(Icons.star_border_purple500),onTap: (){ Navigator.pop(context); },)) ], ), const Divider(), const SizedBox(height: 10 ,), Container( padding: const EdgeInsets.all(10 ), width: double .infinity, child: const Text("提示信息" ,style: TextStyle( fontSize: 40 ),), ) ], ), ), ) ); } }
传递方法和参数
使用dialog
1 2 3 4 5 6 7 8 9 void _mydialog() { showDialog(context: context, builder:(context){ return Mydialog(title:"提示" ,content:"我是提示" ,onTab: (){ Navigator.pop(context); print ("关闭" ); },); }); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Mydialog extends Dialog { String title; String content; final Function ()? onTab; Mydialog({required this .onTab, required this .title,required this .content,super .key}); @override Widget build(BuildContext context){ return Material( type: MaterialType.transparency, child: Center( child: Container( height: 300 , width: 300 , color: Colors.white, child: Column( children: [ const SizedBox(height: 20 ,), Stack( children: [ Align(alignment: Alignment.centerLeft,child: Text(title),), Align(alignment: Alignment.centerRight,child: InkWell(onTap: onTab!(), child:const Icon(Icons.star_border_purple500))) ], ), const Divider(), const SizedBox(height: 10 ,), Container( padding: const EdgeInsets.all(10 ), width: double .infinity, child: Text(content,style: const TextStyle( fontSize: 40 ),), ) ], ), ), ) ); } }
9.PageView 1.PageView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import 'package:flutter/material.dart' ;class MyPageView extends StatefulWidget { const MyPageView({super .key}); @override State<MyPageView> createState() => _MyPageView(); } class _MyPageView extends State <MyPageView > { @override Widget build(BuildContext context) { return Scaffold( body: PageView( children: [ Center( child: Text("1" ,style: Theme.of(context).textTheme.headlineLarge), ), Center( child: Text("2" ,style: Theme.of(context).textTheme.headlineLarge), ), Center( child: Text("3" ,style: Theme.of(context).textTheme.headlineLarge), ), Center( child: Text("4" ,style: Theme.of(context).textTheme.headlineLarge), ), ], ), ); } }
2.PageView.Builder 1 2 3 4 5 6 7 8 return PageView.builder( itemCount: 10 , itemBuilder: (context,index){ return Center( child: Text("data${index+1 } " ,style: Theme.of(context).textTheme.headlineLarge) ); } );
allowImplicitScrolling 缓存当前页面的前后两页
onpageChanged(index){ //页面改变时触发
}
3.轮播图
创建定时器
1 2 3 4 Timer timer = Timer.periodic(const Duration (seconds: 3 ),(timer){ print ("执行" ); });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class MyPageView extends StatefulWidget { const MyPageView({super .key}); @override State<MyPageView> createState() => _MyPageView(); } class _MyPageView extends State <MyPageView > { var _pageController; late Timer timer; var _currentIndex; @override void initState() { super .initState(); _pageController =PageController(initialPage: 0 ); timer = Timer.periodic(const Duration (seconds: 3 ),(timer){ _pageController.animateToPage((_currentIndex+1 )%10 , uration: const Duration (milliseconds: 400 ), curve: Curves.linear,); }); } @override Widget build(BuildContext context) { return PageView.builder( controller: _pageController, itemCount: 10 , itemBuilder: (context,index){ return Center( child: Text("data${index+1 } " ,style: Theme.of(context).textTheme.headlineLarge) ); } ); } @override void dispose() { super .dispose(); timer.cancel(); _pageController.dispose(); } }
4.AutomaticKeepAliveClientMixin
1 2 3 4 5 6 7 class _MyPageView extends State <MyPageView > with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true ; }
10.Flutter Key的作用 1.key的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 void main(){ runApp(const HomeState()); } class HomeState extends StatefulWidget { const HomeState({super .key}); @override State<HomeState> createState()=>_HomeState(); } class _HomeState extends State <HomeState > { List <Widget> list = [ const Boxed(color: Colors.red), const Boxed(color: Colors.yellow), const Boxed(color: Colors.blue,) ]; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: (){ setState(() { list.shuffle(); }); }), appBar: AppBar( title: const Text("标题" ), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: list ), ), ) ); } } class Boxed extends StatefulWidget { final Color color; const Boxed({super .key,required this .color}); @override State<Boxed> createState() => _Boxed(); } class _Boxed extends State <Boxed > { int _cout = 0 ; @override Widget build(BuildContext context) { return SizedBox( height: 100 , width: 100 , child: ElevatedButton( style: ButtonStyle( backgroundColor: WidgetStateProperty.all(widget.color) ), onPressed: (){ setState(() { _cout++; }); }, child: Text("$_cout " ,style: Theme.of(context).textTheme.headlineLarge,)), ); } }
当点击按钮改变顺序时数字没有跟着一起变
LocalKey局部键
Globalkey全局键
1 2 3 4 5 List <Widget> list = [ const Boxed(key: ValueKey("1" ),color: Colors.red), const Boxed(key: ValueKey("2" ),color: Colors.yellow), const Boxed(key: ValueKey("3" ),color: Colors.blue,) ];
获取屏幕方向
1 print (MediaQuery.of(context).orientation);
给子widget传递globalkey使用globalkey获取子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class HomeState extends StatefulWidget { const HomeState({Key? key}) : super (key:key); @override State<HomeState> createState()=>_HomeState(); } class _HomeState extends State <HomeState > { final GlobalKey _globalKey = new GlobalKey(); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ var BoxState = _globalKey.currentState as _Boxed; print (BoxState._cout); }), appBar: AppBar( title: const Text("标题" ), ), body: Boxed(key: _globalKey,color: Colors.red) ) ); } }
11.AnimatedList实现动态列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class _HomeState extends State <HomeState > { List <String > list = ["第一" ,"第二" ]; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { list.add("value" ); }); },child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: AnimatedList(initialItemCount: list.length,itemBuilder: ((context,index,animation){ return FadeTransition(opacity: animation, child: ListTile(title: Text(list[index]),), ); })) ) ); } }
增加数据要使用insertItem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class _HomeState extends State <HomeState > { final _globalKey = GlobalKey<AnimatedListState>(); List <String > list = ["第一" ,"第二" ]; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { list.add("value" ); _globalKey.currentState!.insertItem(list.length-1 ); }); },child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: AnimatedList( key: _globalKey, initialItemCount: list.length,itemBuilder: ((context,index,animation){ return FadeTransition(opacity: animation, child: ListTile(title: Text(list[index]),), ); })) ) ); } }
删除数据使用removeItem
1 2 3 _globalKey.currentState!.removeItem(list.length-1 ,(context,animation){ return FadeTransition(opacity: animation,child: widget,); });
12.flutter动画组件 1.隐式动画
在动画组件内,直接配置curve和duration属性
AnimatedContainer
使用AnimatedContainer组件,配置curve曲线过渡和duration过渡时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class HomeState extends StatefulWidget { const HomeState({Key? key}) : super (key:key); @override State<HomeState> createState()=>_HomeState(); } class _HomeState extends State <HomeState > { bool press = false ; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { press = true ; }); } ,child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: AnimatedContainer( curve: Curves.ease, duration: const Duration (seconds: 1 ), width: press ? 200 : 300 , height: 200 , color:Colors.yellow, transform: press ? Matrix4.translationValues(0 , 0 , 0 ) : Matrix4.translationValues(100 , 100 , 0 ) ), ) ) ); } }
AnimatedPadding
通过配置padding值的改变,引起组件的移动动画效果,同样支持curve和duration的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class _HomeState extends State <HomeState > { bool press = false ; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { press = true ; }); } ,child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: AnimatedPadding( padding: EdgeInsets.fromLTRB(10 , press ? 10 : 400 , 0 , 0 ), curve: Curves.ease, duration: const Duration (seconds: 1 ), child: Container( width: 200 , height: 200 , color:Colors.yellow, ), ), ) ) ); } }
AnimatedAlign
通过配置alignment值的改变,引起组件的对齐动画效果,同样支持curve和duration的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class _HomeState extends State <HomeState > { bool press = false ; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { press = true ; }); } ,child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: AnimatedAlign( alignment: press ? Alignment.center : Alignment.topCenter, curve: Curves.ease, duration: const Duration (seconds: 1 ), child: Container( width: 200 , height: 200 , color:Colors.yellow, ), ), ) ) ); } }
AnimatedOpacity
通过配置opacity值的改变,引起组件的透明度变化动画效果,同样支持curve和duration的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class _HomeState extends State <HomeState > { bool press = false ; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { press = true ; }); } ,child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: AnimatedOpacity( opacity: press ? 1 : 0.1 , curve: Curves.ease, duration: const Duration (seconds: 1 ), child: Container( width: 200 , height: 200 , color:Colors.yellow, ), ), ) ) ); } }
AnimatedPositioned
通过配置top,left,right,bottom值的改变,引起组件的距离变化动画效果,同样支持curve和duration的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class _HomeState extends State <HomeState > { bool press = false ; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ setState(() { press = true ; }); } ,child: const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body:Stack( children: [ AnimatedPositioned( top: press ? 0 : 100 , left:press ? 0 : 100 , curve: Curves.ease, duration: const Duration (seconds: 1 ), child: Container( width: 200 , height: 200 , color:Colors.yellow, ), ), ], ) ) ); } }
2.显式动画
使用显示动画,定义AnimationController,在组件上with SingleTickerProviderStateMixin
RotationTransition
RotationTransition实现旋转动画,turns为AnimationController,可以在initState初始化的时候设置vsync,duration来让程序和手机的刷新频率统一,使用..联级操作调用repeat函数让动画重复运动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class _Boxed extends State <Boxed > with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super .initState(); _controller = AnimationController( vsync: this , duration: const Duration (seconds: 1 ) )..repeat(); } @override Widget build(BuildContext context) { return SizedBox( height: 100 , width: 100 , child: RotationTransition(turns: _controller, child: const FlutterLogo(size: 60 ), ) ); } }
AnimationController
controller的播放方式
repeat 重复
forward 播放一次
reverse 倒序播放
stop 停止
reset 重置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super .initState(); _controller = AnimationController(vsync: this , duration: const Duration (seconds: 1 )); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ _controller.repeat(); },child:const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: Column( children: [RotationTransition( turns: _controller, child: const FlutterLogo(size: 60 ), ), ElevatedButton(onPressed: (){ _controller.forward(); }, child:const Icon(Icons.refresh)), ElevatedButton(onPressed: (){ _controller.reverse(); }, child:const Icon(Icons.refresh)), ElevatedButton(onPressed: (){ _controller.stop(); }, child:const Icon(Icons.refresh)), ElevatedButton(onPressed: (){ _controller.reset(); }, child:const Icon(Icons.refresh)), ] ) ) ) ); } }
FadeTransition
RotationTransition实现透明度变化动画,opacity为AnimationController,可以在initState初始化的时候设置vsync,duration来让程序和手机的刷新频率统一,同时controller为0-1的值,因此opacity会发生从透明到实体的过程,如果要实现实体逐渐到透明可以使用reverse()倒序播放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super .initState(); _controller = AnimationController(vsync: this , duration: const Duration (seconds: 1 )); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ _controller.repeat(); },child:const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: FadeTransition(opacity: _controller, child: const FlutterLogo(size: 60 ,), ) ) ) ); } }
也可以通过lowerBound和upperBound来配置controller的最低和最高值
ScaleTransition
ScaleTransition实现放大缩小动画,scale为AnimationController,可以在initState初始化的时候设置vsync,duration来让程序和手机的刷新频率统一,同时controller为0-1的值,因此scale会发生从小到大的过程,如果要实现大逐渐到小可以使用reverse()倒序播放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super .initState(); _controller = AnimationController(vsync: this , duration: const Duration (seconds: 1 )); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ _controller.repeat(); },child:const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: ScaleTransition(scale: _controller, child: const FlutterLogo(size: 60 ,), ) ) ) ); } }
_controller.drive(Tween(begin:0.5,end: 1.5)), 即为开始时30 结束后为90 (初始为60)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super .initState(); _controller = AnimationController(vsync: this , duration: const Duration (seconds: 1 ), lowerBound: 0.5 , upperBound: 1 ); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ _controller.repeat(); },child:const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: ScaleTransition(scale: _controller.drive(Tween(begin:0.5 ,end: 1.5 )), child: const FlutterLogo(size: 60 ,), ) ) ) ); } }
SlideTransition
SlideTransition实现位移变化动画,position为AnimationController,可以在initState初始化的时候设置vsync,duration来让程序和手机的刷新频率统一,使用drive函数内部配置Tween()子组件Offset()设置初始位置和结束位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super .initState(); _controller = AnimationController(vsync: this , duration: const Duration (seconds: 1 ), lowerBound: 0.5 , upperBound: 1 ); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton:FloatingActionButton(onPressed: (){ _controller.repeat(); },child:const Icon(Icons.add),) , appBar: AppBar( title: const Text("测试" ), ), body: Center( child: SlideTransition(position: _controller.drive(Tween(begin: const Offset(0 , 0 ),end: const Offset(1 , 1 ))), child: const FlutterLogo(size: 60 ,), ) ) ) ); }
配置曲线
chain函数内部使用CurveTween组件,同时可以继续使用链式操作继续配置动画
1 child: SlideTransition(position: Tween(begin: const Offset(0 , 0 ),end: const Offset(0 ,2 )).chain(CurveTween(curve: Curves.linear)).animate(_controller),
3.交错式动画
通过配置controller.drive()方法来配置交错动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; bool flag = false ; @override void initState() { super .initState(); _controller = AnimationController( vsync: this , duration: const Duration (seconds: 1 , milliseconds: 1 ), ); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton: FloatingActionButton( onPressed: () { flag? _controller.forward() : _controller.reverse(); flag = !flag; }, child: const Icon(Icons.add), ), appBar: AppBar( title: const Text("测试" ), ), body: Center( child: Stack( children: [ ScaleTransition( scale: _controller.drive(Tween(begin: 0.0 , end: 1.0 ) .chain(CurveTween(curve: const Interval(0.5 , 1 )))), child: const Icon(Icons.search)), ScaleTransition( scale: _controller.drive(Tween(begin: 1.0 , end: 0.0 ) .chain(CurveTween(curve: const Interval(0 , 0.5 )))), child: const Icon(Icons.close), ) ], )))); } }
交错动画实现方块位移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class _HomeState extends State <HomeState > with SingleTickerProviderStateMixin { late AnimationController _controller; bool flag = false ; @override void initState() { super .initState(); _controller = AnimationController( vsync: this , duration: const Duration (seconds: 1 , milliseconds: 1 ), ); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( floatingActionButton: FloatingActionButton( onPressed: () { flag? _controller.forward() : _controller.reverse(); flag = !flag; }, child: const Icon(Icons.add), ), appBar: AppBar( title: const Text("测试" ), ), body: Center( child: Column( children: [ SlideTransition(position: _controller.drive(Tween(begin: const Offset(0.0 , 0.0 ),end: const Offset(1.5 , 0 )).chain(CurveTween(curve: Curves.ease)).chain(CurveTween(curve: const Interval(0 , 0.2 )))), child: Container( width: 100 , height: 100 , color: Colors.blue[100 ], ), ), SlideTransition(position: _controller.drive(Tween(begin: const Offset(0.0 , 0.0 ),end: const Offset(1.5 , 0 )).chain(CurveTween(curve: Curves.ease)).chain(CurveTween(curve: const Interval(0.2 , 0.4 )))), child: Container( width: 100 , height: 100 , color: Colors.blue[300 ], ), ), SlideTransition(position: _controller.drive(Tween(begin: const Offset(0.0 , 0.0 ),end: const Offset(1.5 , 0 )).chain(CurveTween(curve: Curves.ease)).chain(CurveTween(curve: const Interval(0.4 , 0.6 )))), child: Container( width: 100 , height: 100 , color: Colors.blue[600 ], ), ), SlideTransition(position: _controller.drive(Tween(begin: const Offset(0.0 , 0.0 ),end: const Offset(1.5 , 0 )).chain(CurveTween(curve: Curves.ease)).chain(CurveTween(curve: const Interval(0.6 , 0.8 )))), child: Container( width: 100 , height: 100 , color: Colors.blue[900 ], ), ), ], ), ) )); }
4.Hero动画
在路由组件内配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 class _HomeState extends State <HomeState > { Map routes = { "/" : (context) => const MyHomePage(), "/news" : (context, {arguments}) => NewsPage(arguments: arguments), "/setting" : (context, {arguments}) => SettingPage(arguments: arguments), "/page" :(context) => const MyPageView() }; @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false , onGenerateRoute: (RouteSettings settings){ final String? name = settings.name; final Function? builder = routes[name]; if (builder != null ){ if (settings.arguments != null ){ final Route route = CupertinoPageRoute( builder: (context) => builder(context,arguments:settings.arguments) ); return route; }else { final Route route = CupertinoPageRoute( builder: (context) => builder(context) ); return route; } } return null ; }, title: "richu" , initialRoute: "/" , home: RouteState(), ); } } class RouteState extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ListView( children: [ ElevatedButton(onPressed: (){ Navigator.pushNamed(context, "/news" ,arguments: { "title" :"组件news" , "aid" :20 }); }, child: const Hero(tag: "组件news" , child:Text("text" ))), ], ) ), ); } }
在子路由组件中使用hero组件接收参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class _NewsPage extends State <NewsPage > { return ListView( children: [Hero(tag:widget.arguments["title" ], child:Text(widget.arguments["title" ])),ElevatedButton(onPressed: (){ Navigator.of(context).pushReplacementNamed("/" ); }, child: const Text("注册" )),const SizedBox(height: 20 ,),ElevatedButton(onPressed: (){ _alertDialog(); }, child: const Text("取消" )),const SizedBox(height: 20 ,),ElevatedButton(onPressed: (){ _simpleDialog(); }, child: const Text("提示信息" )),const SizedBox(height: 20 ,),ElevatedButton(onPressed: (){ _modelDialog(); }, child: const Text("显示" )),const SizedBox(height: 20 ,),ElevatedButton(onPressed: (){ _mydialog(); }, child: const Text("提示" ))], ); } }
13.Getx 1.基本使用 1.状态管理
GetX 高性能的状态管理,智能的依赖注入和便捷的路由管理
get: ^4.6.1
flutter pub get
1 2 3 4 5 6 7 8 9 10 Get.defaultDialog( title: "提示信息" , middleText: "确定删除?" , confirm: ElevatedButton(onPressed: (){ print ("ok" ); }, child: const Text("确定" )), cancel: ElevatedButton(onPressed: (){ print ("test" ); }, child: const Text("确定" )) );
返回上一级
2.组件的使用
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class HomeState extends StatelessWidget { const HomeState({super .key}); @override Widget build(BuildContext context) { return GetMaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), themeMode: ThemeMode.light, debugShowCheckedModeBanner: false , home: Scaffold( appBar: AppBar( title: const Text("data" ), ), body: const PageGet(), ), ); } }
defaultDialog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @override Widget build(BuildContext context) { return Center( child: ElevatedButton( onPressed: () { Get.defaultDialog( title: "提示信息" , middleText: "确定删除?" , confirm: ElevatedButton(onPressed: (){ Get.back(); }, child: const Text("确定" )), cancel: ElevatedButton(onPressed: (){ Get.back(); }, child: const Text("取消" )) ); }, child: const Text("Show Dialog" ), ), ); }
snackbar 默认在顶部
1 2 3 Get.snackbar("警告" , "未知错误" ); Get.snackbar("警告" , "未知错误" ,snackPosition: SnackPosition.BOTTOM);
bottomSheet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Get.bottomSheet(Container( color: Colors.white, height: 200 , child: Column( children: [ ElevatedButton(onPressed: (){}, child: const ListTile( leading: Icon(Icons.sunny), title: Text("白天模式" ,style: TextStyle(color: Colors.black),), ),), ElevatedButton(onPressed: (){}, child:const ListTile( leading: Icon(Icons.nightlight), title: Text("黑夜模式" ,style: TextStyle(color: Colors.black),), )) ], ), ));
3.通过getx切换主题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Get.bottomSheet(Container( color: Colors.white, height: 200 , child: Column( children: [ ListTile( leading: Icon(Icons.sunny), onTap: (){ Get.changeTheme(ThemeData.light()); Get.back(); }, title: const Text("白天模式" ,style: TextStyle(color: Colors.black),), ), ListTile( leading: Icon(Icons.nightlight), onTap: (){ Get.changeTheme(ThemeData.dark()); Get.back(); }, title:const Text("黑夜模式" ,style: TextStyle(color: Colors.black),), ) ], ), ));
app
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class HomeState extends StatelessWidget { const HomeState({super .key}); @override Widget build(BuildContext context) { return GetMaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), themeMode: ThemeMode.light, debugShowCheckedModeBanner: false , home: Scaffold( appBar: AppBar( title: const Text("data" ), ), body: const PageGet(), ), ); } }
4.GetX进行路由切换
配置默认的路由切换动画
1 2 initialRoute: "/" , defaultTransition: Transition.rightToLeft,
Get.toNamed(“/login”) 去到指定路由
Get.offAll(const Tabs(index:4)) 去到根路径
Get.off(NextScreen()); 进入下一个页面但是没有返回按钮
Get.back() 返回
5.Getx配置路由
配置路由,参数可在组件内获取
rightToLeft来配置动画效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class HomeState extends StatelessWidget { const HomeState({super .key}); @override Widget build(BuildContext context) { return GetMaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), themeMode: ThemeMode.light, debugShowCheckedModeBanner: false , initialRoute: "/" , getPages: [ GetPage(name: "/home" , page: () => const MyHomePage()), GetPage( name: "/user" , page: () => const UserPage(), transition: Transition.rightToLeft) ], home: Scaffold( appBar: AppBar( title: const Text("data" ), ), body: const PageGet(), ), ); } }
传递参数
1 Get.toNamed("/news" ,arguments: {"title" :"richu" });
接收参数
1 Get.arguments["title" ], child: Text(Get.arguments["title" ]
6.中间件
定义中间件
1 2 3 4 5 6 7 8 class ShopMiddleware extends GetMiddleware { RouteSettings? redirect(String? route){ return null ; return const RouteSettings(name: "/" ,arguments: {}); } }
配置中间件
1 2 3 4 5 6 7 8 GetPage( name: "/user" , page: () => const UserPage(), transition: Transition.rightToLeft, middlewares: [ ShopMiddleware() ] ),
配置多个中间件,并且配置优先级
1 ShopMiddleware(priority: 2 )
1 2 3 4 5 6 7 8 9 class ShopMiddleware extends GetMiddleware { ShopMiddleware({super .priority}); RouteSettings? redirect(String? route){ return const RouteSettings(name: "/" ,arguments: {}); } }
2.状态管理
定义一个响应式变量
1 2 RxInt _counter = 0 .obs; RxString _name = "" .obs;
监听
1 2 children: [Obx(()=>Text("$_counter .value" ))], Obx(()=>Text(_name.value))
定义响应式变量的方法
1 2 Rx<String > _test1 = Rx<String >("test" ); RxString _test2 = RxString("test" );
3.响应式状态管理
多个页面共享数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import 'package:get/get.dart' ;class CounterController extends GetxController { RxInt counter = 0 .obs; inc(){ counter++; update(); } dec(){ counter--; update(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class _PageGet extends State <PageGet > { CounterController counterController = Get.put(CounterController()); @override Widget build(BuildContext context) { return Column( children: [ Obx(() => Text("${counterController.counter} " )), ElevatedButton( onPressed: () { counterController.inc(); }, child: const Text("++" )), ElevatedButton( onPressed: () { Get.toNamed("/news" , arguments: {"title" : "richu" }); }, child: const Text("to news" )) ], ); } }
获取
1 2 3 4 5 6 CounterController counterController = Get.find(); ElevatedButton(onPressed: (){ counterController.inc(); }, child:const Text("++" )), Obx(()=>Text("${counterController.counter} " ))
2.Binding
1 2 3 4 5 6 7 8 9 import 'package:get/get.dart' ;import 'package:richu/controllers/counter.dart' ;class AllControllerBinding extends Bindings { @override void dependencies() { Get.lazyPut<CounterController>(()=>CounterController()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 return GetMaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), themeMode: ThemeMode.light, debugShowCheckedModeBanner: false , initialRoute: "/" , defaultTransition: Transition.rightToLeft, initialBinding: AllControllerBinding(), getPages: [ GetPage( name: "/home" , page: () => const MyHomePage(), transition: Transition.rightToLeft), GetPage( name: "/user" , page: () => const UserPage(), transition: Transition.rightToLeft, middlewares: [ShopMiddleware(priority: 2 )]), GetPage( name: "/news" , page: () => const NewsPage(), transition: Transition.rightToLeft) ], home: Scaffold( appBar: AppBar( title: const Text("data" ), ), body: const PageGet(), ), );
配置之后无序put
4.使用GetView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import 'package:flutter/material.dart' ;import 'package:flutter/src/widgets/framework.dart' ;import 'package:get/get.dart' ;import 'package:richu/controllers/counter.dart' ;class CounterPage extends GetView <CounterController > { const CounterPage({super .key}); @override Widget build(BuildContext context) { Get.put(CounterController()); return Scaffold( appBar: AppBar( title: const Text("title" ), ), body: Center( child: Column( children: [ Obx(()=>Text("${controller.counter} " )), const SizedBox(height: 20 ,), ElevatedButton(onPressed: (){ controller.inc(); }, child: const Text("++" )) ], ), ), ); } }
在路由注入controller
1 GetPage(name: "/counter" , page: () => const CounterPage(),binding:Counterbinding())
初始化后可以直接使用,无需put
5.controller的生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @override void onInit() { print ("init" ); } @override void onReady() { print ("ready" ); } @override void onClose() { print ("close" ); }
6.Flutter实现多语言配置 1 2 3 4 5 ElevatedButton(onPressed: () {}, child: const Text("切换到中文" )), const SizedBox( height: 20 , ), ElevatedButton(onPressed: () {}, child: const Text("切换到英文" ))
定义语言
1 2 3 4 5 6 7 8 9 10 11 12 13 import 'package:get/get.dart' ;class Message extends Translations { @override Map <String , Map <String , String >> get keys =>{ 'zh_CN' :{ 'hello' :"你好" }, 'en-US' :{ 'hello' :"hello richu" } }; }
1 2 3 4 5 6 7 8 9 10 return GetMaterialApp( theme: ThemeData.light(), darkTheme: ThemeData.dark(), themeMode: ThemeMode.light, debugShowCheckedModeBanner: false , initialRoute: "/" , translations: Message(), locale: const Locale('zh' ,'CN' ), fallbackLocale: const Locale('en' ,'US' ), defaultTransition: Transition.rightToLeft,)
切换语言
1 2 3 4 5 6 7 8 9 10 11 ElevatedButton(onPressed: () { var locale = const Locale("zh" ,'CN' ); Get.updateLocale(locale); }, child: const Text("切换到中文" )), const SizedBox( height: 20 , ), ElevatedButton(onPressed: () { var locale = const Locale("en" ,'US' ); Get.updateLocale(locale); }, child: const Text("切换到英文" )),
使用多语言
7.GetUtils工具库
GetUtils
是getx
为我们提供一些常用的工具类库,包括值是否为空 、是否是数字 、是否是视频、图片、音频、PPT、Word、APK 、邮箱、手机号码、日期、MD5、SHA1 等等。
验证邮箱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class _PageGet extends State <PageGet > { TextEditingController textEditingController = TextEditingController(); @override Widget build(BuildContext context) { return Column( children: [ TextField( controller: textEditingController, ), ElevatedButton(onPressed: (){ if (GetUtils.isEmail(textEditingController.text)){ Get.snackbar("正确" , "验证成功" ); }else { Get.snackbar("错误" , "错误的邮箱" ,colorText: Colors.red[100 ]); } }, child: const Text("验证邮箱" )) ], ); } }
验证手机号
1 2 3 4 5 6 7 8 9 10 TextField( controller: textEditingController, ), ElevatedButton(onPressed: (){ if (GetUtils.isPhoneNumber(textEditingController.text) && textEditingController.text.length == 11 ){ Get.snackbar("正确" , "验证成功" ); }else { Get.snackbar("错误" , "错误的手机号" ,colorText: Colors.red[100 ]); } }, child: const Text("验证手机号" )),
14.sqllite 1. 添加依赖 为了使用 SQLite 数据库,首先需要导入 sqflite 和 path package。
sqflite 提供了丰富的类和方法,以便你能便捷实用 SQLite 数据库。
path 提供了大量方法,以便你能正确的定义数据库在磁盘上的存储位置。
运行 flutter pub add 将其添加为依赖:
1 flutter pub add sqflite path
2. 打开数据库 在你准备读写数据库的数据之前,你要先打开这个数据库。打开一个数据库有以下两个步骤:
使用 sqflite package 里的 getDatabasesPath 方法并配合 path package里的 join 方法定义数据库的路径。
使用 sqflite package 里的 openDatabase 方法打开数据库。
提示
1 2 3 4 5 6 7 WidgetsFlutterBinding.ensureInitialized(); final database = openDatabase( join(await getDatabasesPath(), 'dog.db' ), );
3. 创建 dogs 表 接下来,你需要创建一个表用以存储各种狗狗的信息。在这个示例中,创建一个名为 dogs 数据库表,它定义了可以被存储的数据。这样,每条 Dog 数据就包含了一个 id, name 和 age。因此,在 dogs 数据库表中将有三列,分别是 id, name 和 age。
id 是 Dart 的 int 类型,在数据表中是 SQLite 的 INTEGER 数据类型。最佳实践是将 id 作为数据库表的主键,用以改善查询和修改的时间。
name 是Dart的 String类型,在数据表中是SQLite的 TEXT 数据类型。
age 也是Dart的 int 类型,在数据表中是SQLite的 INTEGER 数据类型。
关于 SQLite 数据库能够存储的更多的数据类型信息请查阅官方的 SQLite Datatypes 文档。
1 2 3 4 5 6 7 8 9 10 11 12 final database = openDatabase( join(await getDatabasesPath(), 'dog.db' ), onCreate: (db, version) { return db.execute( 'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)' , ); }, version: 1 , );
4. 插入一条狗狗的数据 现在你已经准备好了一个数据库用于存储各种狗狗的信息数据,现在开始读写数据咯。
首先,在 dogs 数据表中插入一条 Dog 数据。分以下两步:
把 Dog 转换成一个 Map 数据类型;
使用 insert() 方法把 Map 保存到 dogs 数据表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Dog { final int id; final String name; final int age; Dog({required this .id, required this .name, required this .age}); Map <String , Object? > toMap() { return {'id' : id, 'name' : name, 'age' : age}; } @override String toString() { return 'Dog{id: $id , name: $name , age: $age }' ; } }
1 2 3 4 5 6 7 8 9 10 Future<void > insertDog(Dog dog) async { final db = await database; await db.insert( 'dogs' , dog.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); }
插入一条数据
1 2 3 var fido = Dog(id: 0 , name: 'Fido' , age: 35 );await insertDog(fido);
5. 查询狗狗列表 现在已经有了一条 Dog 存储在数据库里。你可以通过查询数据库,检索到一只狗狗的数据或者所有狗狗的数据。分为以下两步:
调用 dogs 表对像的 query 方法。这将返回一个List 。
将 List 转换成 List 数据类型。
query函数的参数
1 2 3 4 5 6 7 8 9 10 void query(String table, {bool? distinct, List <String >? String? where, List <Object? >? whereArgs, String? groupBy, String? having, String? orderBy, int? limit, int? offset});
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import 'package:sqflite/sqflite.dart' ;Database database; List <Map <String , dynamic >> results = await database.query('users' );results = await database.query('users' , columns: ['id' , 'name' ]); results = await database.query( 'users' , where: 'age > ?' , whereArgs: [18 ], ); results = await database.query( 'users' , columns: ['age' ], groupBy: 'age' , having: 'COUNT(*) > 1' , orderBy: 'age DESC' , ); results = await database.query( 'users' , limit: 10 , offset: 20 , ); results = await database.query( 'users' , distinct: true , columns: ['email' ], );
综合示例
1 2 3 4 5 6 7 8 9 10 11 12 results = await database.query( 'users' , distinct: true , columns: ['id' , 'name' , 'email' ], where: 'age >= ? AND active = ?' , whereArgs: [20 , 1 ], groupBy: 'department' , having: 'COUNT(*) > 5' , orderBy: 'name ASC' , limit: 50 , offset: 0 , );
1 2 3 4 5 6 7 8 9 10 11 Future<List <Dog>> dogs() async { final db = await database; final List <Map <String , Object? >> dogMaps = await db.query('dogs' ); return [ for (final {'id' : id as int , 'name' : name as String , 'age' : age as int } in dogMaps) Dog(id: id, name: name, age: age), ]; }
7. 修改一条 Dog 数据 使用 sqflite package 中的 update()方法,可以对已经插入到数据库中的数据进行修改(更新)。
修改数据操作包含以下两步:
将一条狗狗的数据转换成 Map 数据类型;
使用 where 语句定位到具体将要被修改的数据。
1 2 3 4 5 6 7 8 9 Future<void > updateDog(Dog dog) async { final db = await database; await db.update( 'dogs' , dog.toMap(), where: 'id = ?' , whereArgs: [dog.id], ); }
1 2 3 4 fido = Dog(id: fido.id, name: fido.name, age: fido.age + 7 ); await updateDog(fido);print (await dogs());
请注意 使用 whereArgs 将参数传递给 where 语句。有助于防止 SQL 注入攻击。
这里不要使用字符串模板,比如: where: “id = ${dog.id}”!
8. 删除一条 Dog 的数据 除了插入和修改狗狗们的数据,你还可以从数据库中删除狗狗的数据。删除数据用到了 sqflite package 中的 delete() 方法。
在这一小节,新建一个方法用来接收一个 id 并且删除数据库中与这个 id 匹配的那一条数据。为了达到这个目的,你必须使用 where 语句限定哪一条才是被删除的数据。
1 2 3 4 5 6 7 8 9 Future<void > deleteDog(int id) final db = await database; await db.delete( 'dogs' , where: 'id = ?' , whereArgs: [id], ); }
在Flutter中使用Getx进行状态管理时,可以通过以下方法确保Controller全局不销毁:
使用permanent参数:在创建Controller时,将permanent参数设置为true。这样,Controller会被标记为不可销毁,页面销毁时不会自动销毁Controller。
1 Get.put<YourController>(YourController(), permanent: true );
手动销毁Controller:如果需要手动销毁Controller,可以调用Get.delete()方法。在需要销毁Controller时,可以这样操作:
1 Get.delete<YourController>();
生命周期管理:了解Controller的生命周期方法,如onInit、onReady、onClose等,确保在onClose方法中正确释放资源,以避免内存泄漏。
使用Get.lazyPut:与Get.put类似,但Get.lazyPut会在第一次访问时才创建Controller实例,这在某些情况下也可以帮助管理Controller的生命周期。
15.SharedPreferences 步骤 1:添加依赖 在 pubspec.yaml 文件中添加 shared_preferences 依赖:
1 2 3 4 dependencies: flutter: sdk: flutter shared_preferences: ^2.2.2
然后运行 flutter pub get 来安装依赖。
步骤 2:使用 SharedPreferences 以下是一个完整的代码示例,展示如何存储、读取和删除数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 import 'package:flutter/material.dart' ;import 'package:shared_preferences/shared_preferences.dart' ;void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super .key}); @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State <HomePage > { final String _keyName = "username" ; final String _keyAge = "age" ; final String _keyTheme = "isDarkMode" ; Future<void > _saveData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString(_keyName, "John Doe" ); await prefs.setInt(_keyAge, 30 ); await prefs.setBool(_keyTheme, true ); print ("Data saved successfully!" ); } Future<void > _readData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String? name = prefs.getString(_keyName); int? age = prefs.getInt(_keyAge); bool? isDarkMode = prefs.getBool(_keyTheme); print ("Name: $name , Age: $age , Dark Mode: $isDarkMode " ); } Future<void > _deleteData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.remove(_keyName); await prefs.remove(_keyAge); await prefs.remove(_keyTheme); print ("Data deleted successfully!" ); } Future<void > _clearAllData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.clear(); print ("All data cleared!" ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("SharedPreferences Example" ), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: _saveData, child: const Text("Save Data" ), ), const SizedBox(height: 16 ), ElevatedButton( onPressed: _readData, child: const Text("Read Data" ), ), const SizedBox(height: 16 ), ElevatedButton( onPressed: _deleteData, child: const Text("Delete Data" ), ), const SizedBox(height: 16 ), ElevatedButton( onPressed: _clearAllData, child: const Text("Clear All Data" ), ), ], ), ), ); } }