Flutter学习笔记(六)---状态管理、事件、路由、动画

发布于:2025-08-12 ⋅ 阅读:(12) ⋅ 点赞:(0)

共享状态管理

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的一般使用步骤:

  1. 创建自己需要共享的数据
  2. 在应用程序的顶层使用ChangeNotifierProvider
  3. 在其他位置使用 共享数据 即可

android studio生成get/set方法的快捷键:command+n

有两种方法,可以设置或者获取 共享数据:
Provider.ofConsumer
一般使用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)
            )
        ),
      ),
    );
  }
}