Flutter学习笔记(六)---状态管理、事件、路由、动画
共享状态管理
InheritedWidget
InheritedWidget可以实现跨组件数据的传递
定义一个共享数据的InheritedWidget,需要继承自InheritedWidget
import 'package:flutter/material.dart';
import 'dart:math';/// 创建一个继承InheritedWidget的子类
class YZCounterWidget extends InheritedWidget {// 1. 共享的数据final int counter;// 2. 构造方法YZCounterWidget({required super.child,this.counter = 0});// 3. 获取组件最近的当前InheritedWidgetstatic YZCounterWidget? of(BuildContext context) {/// 沿着Element树,找最近的YZCounterElement,从Element中取出Widget对象return context.dependOnInheritedWidgetOfExactType();}// 4. 决定要不要回调didChangeDependencies 方法 bool updateShouldNotify(YZCounterWidget oldWidget) {return oldWidget.counter != counter;//true执行}
}main() => runApp(MyApp());class MyApp extends StatelessWidget {MyApp({super.key}); Widget build(BuildContext context) {return MaterialApp(home: YZHomePage(),);}
}class YZHomePage extends StatefulWidget { State<YZHomePage> createState() => _YZHomePageState();
}class _YZHomePageState extends State<YZHomePage> {int _counter = 109; Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("InheritedWidget使用"),backgroundColor: Colors.purpleAccent,),body: YZCounterWidget(counter: _counter,child: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [YZHomeShowData01(),YZHomeShowData02(),],),),),floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: (){setState(() {_counter++;});},),);}
}class YZHomeShowData01 extends StatefulWidget {const YZHomeShowData01({super.key}); State<YZHomeShowData01> createState() => _YZHomeShowData01State();
}class _YZHomeShowData01State extends State<YZHomeShowData01> {void didChangeDependencies() {// TODO: implement didChangeDependenciessuper.didChangeDependencies();print("_YZHomeShowData01State执行了didChangeDependencies");} Widget build(BuildContext context) {int? counter = YZCounterWidget.of(context)?.counter;return Card(color: Colors.greenAccent,child: Text("当前计数是:$counter"),);}
}class YZHomeShowData02 extends StatelessWidget {const YZHomeShowData02({super.key}); Widget build(BuildContext context) {int? counter = YZCounterWidget.of(context)?.counter;return Container(decoration: BoxDecoration(color: Colors.amberAccent,),child: Text("当前计数是:$counter"),);}
}
Provider
Provider
的一般使用步骤:
- 创建自己需要共享的数据
- 在应用程序的顶层使用
ChangeNotifierProvider
- 在其他位置使用 共享数据 即可
android studio
生成get/set方法的快捷键:command+n
有两种方法,可以设置或者获取 共享数据:
Provider.of
和 Consumer
一般使用Consumer
,因为如果使用的是Provider.of
,则当改变的时候,整个build方法会重新执行
注意: 点击的时候,直接++,不需要加setState
onPressed: (){contextVM.counter++;
},
import 'package:flutter/material.dart';
import 'dart:math';import 'package:provider/provider.dart';/// 共享数据 with是混入,也就是非继承但是可以有ChangeNotifier所以的方法和属性
class YZCounterViewModel with ChangeNotifier{int _counter = 10;int get counter => _counter;set counter(int value) {_counter = value;/// 通知所有的监听者notifyListeners();}
}main() {runApp(// 单个的// ChangeNotifierProvider(// create: (BuildContext context) {// return YZCounterViewModel();// },// child: MyApp()// ),// 多个的MultiProvider(providers: [ChangeNotifierProvider(create: (BuildContext context) {return YZCounterViewModel();},),// 多个// ...],child: MyApp(),));
}class MyApp extends StatelessWidget {MyApp({super.key}); Widget build(BuildContext context) {return MaterialApp(home: YZHomePage(),);}
}class YZHomePage extends StatefulWidget { State<YZHomePage> createState() => _YZHomePageState();
}class _YZHomePageState extends State<YZHomePage> { Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("InheritedWidget使用"),backgroundColor: Colors.purpleAccent,),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [YZHomeShowData01(),YZHomeShowData02(),YZHomeShowData03(),],),),// 使用Consumer// floatingActionButton: Consumer<YZCounterViewModel> (// builder: (context, contextVM, child){// print("floatingActionButton builder 被执行");// return FloatingActionButton(// child: child,// onPressed: (){// // 不需要加 setState// // setState(() {// // contextVM.counter++;// // });// contextVM.counter++;// },// );// },// child: Icon(Icons.add),//放在这,是为了不被每次重建// )// 使用 SelectorfloatingActionButton: Selector<YZCounterViewModel, YZCounterViewModel> (builder: (context, contextVM, child){print("floatingActionButton builder 被执行");return FloatingActionButton(child: child,onPressed: (){// 不需要加 setState// setState(() {// contextVM.counter++;// });contextVM.counter++;},);},selector: (buildContext, viewModel){return viewModel;},/// 是否需要重新构建shouldRebuild: (previous, next){return false;// 不需要重新构建},child: Icon(Icons.add),//放在这,是为了不被每次重建));}
}class YZHomeShowData01 extends StatefulWidget {const YZHomeShowData01({super.key}); State<YZHomeShowData01> createState() => _YZHomeShowData01State();
}class _YZHomeShowData01State extends State<YZHomeShowData01> {void didChangeDependencies() {// TODO: implement didChangeDependenciessuper.didChangeDependencies();print("_YZHomeShowData01State执行了didChangeDependencies");} Widget build(BuildContext context) {int counter = Provider.of<YZCounterViewModel>(context).counter;print("YZHomeShowData01-build");return Card(color: Colors.greenAccent,child: Text("当前计数是:$counter"),);}
}class YZHomeShowData02 extends StatelessWidget {const YZHomeShowData02({super.key}); Widget build(BuildContext context) {int counter = Provider.of<YZCounterViewModel>(context).counter;print("YZHomeShowData02-build");return Container(decoration: BoxDecoration(color: Colors.amberAccent,),child: Text("当前计数是:$counter"),);}
}class YZHomeShowData03 extends StatelessWidget {const YZHomeShowData03({super.key}); Widget build(BuildContext context) {print("YZHomeShowData03-build");return Container(decoration: BoxDecoration(color: Colors.redAccent,),child: Consumer<YZCounterViewModel>(builder: (context, viewModel, child) {print("YZHomeShowData03-Consumer-build");return Text("当前计数是:${viewModel.counter}");}));}
}
事件
import 'package:flutter/material.dart';
import 'dart:math';main() => runApp(MyApp());class MyApp extends StatelessWidget {MyApp({super.key}); Widget build(BuildContext context) {return MaterialApp(home: YZHomePage(),);}
}class YZHomePage extends StatefulWidget { State<YZHomePage> createState() => _YZHomePageState();
}class _YZHomePageState extends State<YZHomePage> { Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("事件学习"),backgroundColor: Colors.purpleAccent,),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [YZTouchPointer(),SizedBox(height: 20,),YZGestureWidget(),SizedBox(height: 20,),YZGesture2Widget(),])),);}
}class YZTouchPointer extends StatelessWidget {const YZTouchPointer({super.key}); Widget build(BuildContext context) {return Listener(onPointerDown: (event){print("onPointerDown");print(event.position);// 相当于整个屏幕print(event.localPosition); // 相当于本身},onPointerMove: (event){print("onPointerMove");},onPointerUp: (event){print("onPointerUp");},onPointerCancel: (event){print("onPointerCancel");},child: Container(width: 200,height: 200,color: Colors.amber,),);}
}class YZGestureWidget extends StatelessWidget {const YZGestureWidget({super.key}); Widget build(BuildContext context) {return GestureDetector(onTapDown: (event){print("onTapDown");print(event.localPosition);//相当于本控件的print(event.globalPosition);//全局的},onTap: (){print("onTap");},onTapUp: (event){print("onPoionTapUpnterUp");},onTapCancel: (){print("onTapCancel");},onDoubleTap: (){print("onDoubleTap");},onLongPress: (){print("onLongPress");},child: Container(width: 200,height: 200,color: Colors.greenAccent,),);}
}class YZGesture2Widget extends StatelessWidget {const YZGesture2Widget({super.key}); Widget build(BuildContext context) {return Stack(alignment: Alignment.center,children: [GestureDetector(onTapDown: (detail) {print("detail1");},child: Container(width: 200,height: 200,color: Colors.blue,),),GestureDetector(onTapDown: (detail) {print("detail2");},child: Container(width: 100,height: 100,color: Colors.red,),),],);}
}
事件传递
不同组件件,事件如何传递
可以使用第三方库,比如 event_bus
event_bus使用步骤:
// 1. 创建一个全局的EventBus对象
final eventBus = EventBus();// 2. 发出事件
eventBus.fire("This is changed message");// 3. 监听事件
eventBus.on<String>().listen((data){setState(() {message = data;});
});
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';main() => runApp(MyApp());// 1. 创建一个全局的EventBus对象
final eventBus = EventBus();class MyApp extends StatelessWidget {MyApp({super.key}); Widget build(BuildContext context) {return MaterialApp(home: YZHomePage(),);}
}class YZHomePage extends StatefulWidget { State<YZHomePage> createState() => _YZHomePageState();
}class _YZHomePageState extends State<YZHomePage> { Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("事件学习"),backgroundColor: Colors.purpleAccent,),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [YZDemoButton(),YZDemoText()])),);}
}class YZDemoButton extends StatelessWidget {const YZDemoButton({super.key}); Widget build(BuildContext context) {return TextButton(onPressed: (){// 2. 发出事件eventBus.fire("This is changed message");},child: Text("This is a button"),style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.cyan)),);}
}class YZDemoText extends StatefulWidget {const YZDemoText({super.key}); State<YZDemoText> createState() => _YZDemoTextState();
}class _YZDemoTextState extends State<YZDemoText> {String message = "This is a message";void initState() {// TODO: implement initStatesuper.initState();// 3. 监听事件eventBus.on<String>().listen((data){setState(() {message = data;});});} Widget build(BuildContext context) {return Text(message);}
}
路由
普通跳转:Navigator
push到下一个界面:
Future resule = Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){return YZHomeDetailPage(message: "123");})
);// 监听pop返回的信息
resule.then((res){setState(() {this._backMessage = res;});
});
返回pop:
TextButton(onPressed: (){Navigator.of(context).pop("返回按钮携带的信息");
}, child: Text("返回按钮"))
fullscreenDialog: true
是从下往上出现,默认false,左右出现
MaterialPageRoute(builder: (BuildContext context){return YZHomeDetailPage(message: "123");},fullscreenDialog: true)
自定义转场动画
Future resule = Navigator.push(context,PageRouteBuilder(pageBuilder: (context, animation1, animation2){return FadeTransition(opacity: animation1,child: YZHomeDetailPage(message: "123"));},)
);
使用Route跳转
注册路由:
return MaterialApp(home: YZHomePage(),routes: {"/about": (BuildContext context){return Yzhomeaboutview(message: "123",);}},);
使用路由跳转:
Navigator.of(context).pushNamed("/about")
当然,最好在被跳转页里面加上:
static const String routeName = "/about";
这样,定义为静态变量,不依赖对象,而且,只有一份,全局可用,不易写错
Route传值
传递过去:
Navigator.of(context).pushNamed(Yzhomeaboutview.routeName, arguments: "传入到关于的信息");
接收:
final String message = ModalRoute.of(context)?.settings.arguments as String;
当跳转的控制器初始化携带构造函数参数的时候
// 钩子函数onGenerateRoute: (RouteSettings settings){if (settings.name == "/detail") {return MaterialPageRoute(builder: (context){return YZHomeDetailPage(message: settings.arguments as String,);});}return null;},
尽量给个没有界面的Widget,以防没有某个Widget,报错
当然,将Route单独创建一个文件,并将所以路由字符串放进去比较好
动画
做动画的Widget,需要是StatefulWidget
Animation(抽象类)
抽象类,不直接使用
AnimationController(可以使用)
可以直接使用,AnimationController继承Animation
CurvedAnimation
设置动画执行的速率(先快后慢、先慢后快等 )
Tween
设置动画执行value的范围(0-1)
import 'dart:ffi';import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';main() => runApp(MyApp());// 1. 创建一个全局的EventBus对象
final eventBus = EventBus();class MyApp extends StatelessWidget {MyApp({super.key}); Widget build(BuildContext context) {return MaterialApp( home: YZHomePage(),);}
}class YZHomePage extends StatefulWidget { State<YZHomePage> createState() => _YZHomePageState();
}class _YZHomePageState extends State<YZHomePage> with SingleTickerProviderStateMixin{late AnimationController _controller;Animation<double>? _animation;Animation<double>? _sizeAnimation;void initState() {// TODO: implement initStatesuper.initState();_controller = AnimationController(vsync: this,duration: Duration(seconds: 1),// lowerBound: 0.1,// upperBound: 1.0);// 设置Curved(速率)_animation = CurvedAnimation(parent: _controller,curve: Curves.linear,);_sizeAnimation = Tween<double>(begin: 50.0, end: 150.0).animate(_animation! as Animation<double>);// 监听动画// 这个方法不要,因为会刷新所以内容,对性能不好// _controller.addListener((){// setState(() {//// });// });// 监听动画的状态_controller.addStatusListener((AnimationStatus status){if (status == AnimationStatus.completed){_controller.reverse();} else if (status == AnimationStatus.dismissed) {_controller.forward();}});}void dispose() {_controller.dispose(); // 释放资源super.dispose();} Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("动画"),backgroundColor: Colors.purpleAccent,),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Icon(Icons.favorite,color: Colors.red,size: _sizeAnimation?.value,);//;},)],)),floatingActionButton: FloatingActionButton(child: Icon(Icons.refresh),onPressed: (){if (_controller.isAnimating) {_controller.stop();}else {if (_controller.status == AnimationStatus.forward) {_controller.forward();}else if (_controller.status == AnimationStatus.reverse) {_controller.reverse();}else {_controller.forward();}}}), //);}
}
交织动画
即,多个动画一起执行
import 'dart:ffi';
import 'dart:math';import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';main() => runApp(MyApp());// 1. 创建一个全局的EventBus对象
final eventBus = EventBus();class MyApp extends StatelessWidget {MyApp({super.key}); Widget build(BuildContext context) {return MaterialApp(home: YZHomePage(),);}
}class YZHomePage extends StatefulWidget { State<YZHomePage> createState() => _YZHomePageState();
}class _YZHomePageState extends State<YZHomePage> with SingleTickerProviderStateMixin{late AnimationController _controller;Animation<double>? _animation;Animation<double>? _sizeAnimation;Animation<Color>? _colorAnimation;Animation<double>? _opacityAnimation;Animation<double>? _radianAnimation;void initState() {// TODO: implement initStatesuper.initState();_controller = AnimationController(vsync: this,duration: Duration(seconds: 1),);// 设置Curved(速率)_animation = CurvedAnimation(parent: _controller,curve: Curves.linear,);_sizeAnimation = Tween<double>(begin : 50.0, end: 150.0).animate(_controller) as Animation<double>;//_colorAnimation = ColorTween(begin: Colors.lime, end: Colors.red).animate(_controller) as Animation<Color>;_opacityAnimation = Tween<double>(begin : 0.0, end: 1.0).animate(_controller) as Animation<double>;_radianAnimation = Tween<double>(begin : 0.0, end: 1.0).animate(_controller) as Animation<double>;// 监听动画的状态_controller.addStatusListener((AnimationStatus status){if (status == AnimationStatus.completed){_controller.reverse();} else if (status == AnimationStatus.dismissed) {_controller.forward();}});}void dispose() {_controller.dispose(); // 释放资源super.dispose();} Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("动画"),backgroundColor: Colors.purpleAccent,),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child){return Transform(transform: Matrix4.rotationZ(_radianAnimation?.value ?? 0.0),alignment: Alignment.center,child: Container(width: _sizeAnimation?.value,height: _sizeAnimation?.value,color: Colors.blue.withOpacity(_opacityAnimation?.value ?? 0.5),),);},)],)),floatingActionButton: FloatingActionButton(child: Icon(Icons.refresh),onPressed: (){if (_controller.isAnimating) {_controller.stop();}else {if (_controller.status == AnimationStatus.forward) {_controller.forward();}else if (_controller.status == AnimationStatus.reverse) {_controller.reverse();}else {_controller.forward();}}}), //);}
}
颜色的切换没有写对
Hero
即,点击图片大图展示
主要是这两行代码:
Hero(tag: imageUrl,child: Image.network(imageUrl))
Hero例子代码:
import 'package:flutter/material.dart';
import 'dart:math';import 'package:learn_flutter/Animation/image_detail.dart';main() => runApp(MyApp());class MyApp extends StatelessWidget {const MyApp({super.key}); Widget build(BuildContext context) {return const MaterialApp(home: YZHomePage(),);}
}class YZHomeButtonPage extends StatelessWidget {const YZHomeButtonPage({super.key}); Widget build(BuildContext context) {return const Placeholder();}
}class YZHomePage extends StatelessWidget {const YZHomePage({super.key}); Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Widget布局"),backgroundColor: Colors.purpleAccent,),body: YZHomeGridViewContent(),);}
}class YZHomeGridViewContent extends StatefulWidget {const YZHomeGridViewContent({super.key}); State<YZHomeGridViewContent> createState() => _YZHomeGridViewContentState();
}class _YZHomeGridViewContentState extends State<YZHomeGridViewContent> { Widget build(BuildContext context) {return Padding(padding: const EdgeInsets.symmetric(horizontal: 8),// 左右边框8间距child: GridView(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,// 一行3个childAspectRatio: 1.5,// 宽1 高0.5crossAxisSpacing: 10, // 交叉轴间距(这里是左右间距)8mainAxisSpacing: 5,//主轴间距(在这里是上下间距)),children: List.generate(100, (index){final imageUrl = "https://picsum.photos/500/500?random=$index";return GestureDetector(onTap: (){Navigator.of(context).push(PageRouteBuilder(pageBuilder: (context, animation1, animation2){return FadeTransition(opacity: animation1,child: YZImageDetailPage(imageUrl: imageUrl));}));},child: Hero(tag: imageUrl,child: Image.network(imageUrl, fit: BoxFit.fitWidth,)));}) ),);}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';class YZImageDetailPage extends StatelessWidget {final String imageUrl;const YZImageDetailPage({super.key,this.imageUrl = ""}); Widget build(BuildContext context) {return Scaffold(backgroundColor: Colors.black,body: Center(child: GestureDetector(onTap: (){Navigator.of(context).pop();},child: Hero(tag: imageUrl,child: Image.network(imageUrl))),),);}
}