Flutter中Provider的一般用法(一)

在flutter中Provider是比较常用的Widget, Provider通常用来管理value的生命周期,通过Create和Dispose,它们是成对出现的,可以在Create进行value的初始化操作,在dispose进行value的释放操作。使用Provider可以避免一些琐碎的操作,比如实例化一个BLoC操作,事实上,Provider等效于状态管理类State.initState和State.dispose的组合,Create只会在State.initState中调用一次。我们不能直接使用InheritedWidget,因为它需要构造函数已初始化,并且为最终值。

下面是一个例子,初始化一次Model,并且当Provider从数中被移除的时候,dispose会被调用:

 class Model {
   void dispose() {}
 }
 class Stateless extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return Provider<Model>(
       create: (context) =>  Model(),
       dispose: (context, value) => value.dispose(),
       child: ...,
     );
   }
 }

'create'回调是懒加载式的,只有第一次获取value的时候才会被调用,而不是在provider第一次被插入到widget树中的时候调用。当然,这个行为是可以被禁用的,通过设置Provider的"lazy:false"。

Provider提供了几种获取实例的方式:

 Provider({
    Key key,
    @required Create<T> create,
    Dispose<T> dispose,
    bool lazy,
    TransitionBuilder builder,
    Widget child,
  })  : assert(create != null),
        super(
          key: key,
          lazy: lazy,
          builder: builder,
          create: create,
          dispose: dispose,
          debugCheckInvalidValueType: kReleaseMode
              ? null
              : (T value) =>
                  Provider.debugCheckInvalidValueType?.call<T>(value),
          child: child,
        );

这中方式可以创建一个value,并且存储它,和暴露给它的后代,value可以选择性在dispose回调中释放,dispose会在Provider从树中移出的时候被调用。

Provider.value({
    Key key,
    @required T value,
    UpdateShouldNotify<T> updateShouldNotify,
    TransitionBuilder builder,
    Widget child,
  })  : assert(() {
          Provider.debugCheckInvalidValueType?.call<T>(value);
          return true;
        }()),
        super.value(
          key: key,
          builder: builder,
          value: value,
          updateShouldNotify: updateShouldNotify,
          child: child,
        );

这中方式暴露一个已存在的不用释放的值,其中可选参数updateShouldNotify可以用来控制是否需要重绘,当value值改变的时候,一般来说(previous, next) => previous != next时,会重绘。

static T of<T>(BuildContext context, {bool listen = true}) 

这个静态方法可以获取widget树中最近的Provider<T>,并且返回它的值。如果listen为true,那么随后value发生的变化会触发State.build和State.didChangeDependencies. 如果需要在initState中调用Provider.of或者如下调用Provider的create方法时,需要设置listen为false:

Provider(
  create: (context) {
    return Model(Provider.of<Something>(context, listen: false)),
  },
)

如果试图在widget树外监听Provider暴露出来的值,也需要设置listen为false,比如在点击事件中调用,否则的话可能会导致跟事件句柄关联的wieget重建,如果这个widget并不在意这个value的话。

 

讲到Provider的用法,我们还需了解MultiProvider, 一般来说,MultiProvider用于单个线性widget树中,可以增强可读性和减少多层嵌套的样板代码,如下:
 

Provider<Something>(
  create: (_) => Something(),
  child: Provider<SomethingElse>(
    create: (_) => SomethingElse(),
    child: Provider<AnotherThing>(
      create: (_) => AnotherThing(),
      child: someWidget,
    ),
  ),
),

使用MultiProvider则可以简化为:

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
    Provider<AnotherThing>(create: (_) => AnotherThing()),
  ],
  child: someWidget,
)

这两种方式是等效的。下面我们看下MultiProvider的构造方法:

MultiProvider({
    Key key,
    @required List<SingleChildWidget> providers,
    Widget child,
    TransitionBuilder builder,
  })  : assert(providers != null),
        super(
          key: key,
          children: providers,
          child: builder != null
              ? Builder(
                  builder: (context) => builder(context, child),
                )
              : child,
        );

参数builder是个语法糖,用来获取BuildContext,可以用它来获取创建的providers:

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
    Provider<AnotherThing>(create: (_) => AnotherThing()),
  ],
  builder: (context, child) {
    final something = context.watch<Something>();
    return Text('$something');
  },
)

等效于

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
    Provider<AnotherThing>(create: (_) => AnotherThing()),
  ],
  child: Builder(
    builder: (context) {
      final something = context.watch<Something>();
      return Text('$something');
    },
  ),
)

上面两种方式是等效的。当providers中存在provider有child widget的,将会被忽略,下面两种方式是等效的:

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something(), child: SomeWidget()),
  ],
  child: Text('Something'),
)

等效:
MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
  ],
  child: Text('Something'),
)

 

上一篇:02 nacos服务注册与服务发现


下一篇:Android 在低层模块中获取app模块下类的实例(高阶函数、接口)