共享状态管理
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. 获取组件最近的当前InheritedWidget
static 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 didChangeDependencies
super.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),//放在这,是为了不被每次重建
// )
// 使用 Selector
floatingActionButton: 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 didChangeDependencies
super.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 initState
super.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 initState
super.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 initState
super.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.5
crossAxisSpacing: 10, // 交叉轴间距(这里是左右间距)8
mainAxisSpacing: 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)
)
),
),
);
}
}