【问题标题】:Riverpod - How to wrap a PreferredSizeWidget inside a ConsumerRiverpod - 如何在消费者中包装 PreferredSizeWidget
【发布时间】:2021-03-24 07:18:50
【问题描述】:

我有一个DefaultTabController,我有一个返回List<PreferredSizeWidget> 的方法,它们是AppBar 的页面。我希望他们在ChangeNotifier 中观看状态,因此我想将它们包装在Consumer 中。当我尝试这样做时,我收到如下错误:

“参数类型'Widget'不能分配给参数类型'PreferredSizeWidget'。”

我该如何解决这个问题?

感谢和问候。

【问题讨论】:

    标签: flutter dart flutter-provider riverpod


    【解决方案1】:

    错误来自appBar 的参数Scaffold 需要PreferredSizeWidget。我可以想到两个解决方案:

    • 您可以用PreferredSize 包裹您的Consumer,并将Size.fromHeight() 用作preferredSize。也就是说,如果您的应用栏之间的高度是恒定的。
    • 您可以完全避免使用appBar 参数,方法是将Scaffold 的主体与Expanded 包裹在Column 中,并使Consumer 成为其第一个子代。

    【讨论】:

      【解决方案2】:

      这是我基于 Mickael 建议的实现:

      首先我创建了一个 AppBarParams 类来保存 AppBar 状态

      @freezed
      class AppBarParams with _$AppBarParams {
        const factory AppBarParams({
          required String title,
          required List<Widget> actions,
        }) = _AppBarParams;
      }
      

      然后我在全局提供程序文件中创建了一个StateProvider,如下所示:

      final appBarParamsProvider = StateProvider<AppBarParams>((ref) {
        return AppBarParams(title: "Default title", actions: []);
      });
      

      并使用Consumer 将其附加到主应用程序中:

      class MyApp extends StatelessWidget {
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            title: "App Title",
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: SafeArea(
              child: Scaffold(
                appBar: PreferredSize(
                  preferredSize: Size.fromHeight(kToolbarHeight),
                  child: Consumer(
                    builder: (context, watch, child) {
                      final appBarParams = watch(appBarParamsProvider).state;
                      return AppBar(
                        title: Text(appBarParams.title),
                        actions: appBarParams.actions
                      );
                  })
                ),
                body: ... your body widget
              )
            )
          )
        }
      }
      

      然后你只需要编辑提供者状态来相应地更新 AppBar,当用户在页面之间切换时更新 AppBar,我创建了这个 mixin:

      mixin AppBarHandler {
      
        void updateAppBarParams(
          BuildContext context, {
          required String title,
          List<Widget> Function()? actions
        }) {
          WidgetsBinding.instance!.addPostFrameCallback((_) async {
            context.read(appBarParamsProvider).state = context
              .read(appBarParamsProvider).state
              .copyWith(
                 title: title, 
                 actions: actions != null ? actions() : []
              );
          });
        }
      }
      

      在每个必须更改标题或操作的主屏幕视图中,我都这样做了:

      class Page1 extends HookWidget with AppBarHandler {
      
        const Page1({Key? key}) : super(key: key);
      
        @override
        Widget build(BuildContext context) {
          updateAppBarParams(context, 
            title: "Page 1 title",
            actions: () => [
              IconButton(icon: const Icon(Icons.refresh), onPressed: () {
                //a custom action for Page1
                context.read(provider.notifier).updateEntries();
              })
            ]
          );
          ... your screen widget
        }
      }
      

      【讨论】:

        【解决方案3】:

        我建议再次将整个 AppBar 包装在 Consumer 中。您应该使用消费者将小部件包装在 appbar 字段(如前导、标题等)中。 Flutter 的文档中提到将 Consumer 保持在尽可能低的级别。如果您希望它们有条件地出现,您可以遵循以下做法:

        AppBar(actions:[
            Consumer<Provider>(builder: (context,value,child) => value.toShow() ? 
                 MyWidget() : const SizedBox()
                               )
                        ]
               );
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-23
          • 1970-01-01
          • 1970-01-01
          • 2021-01-28
          相关资源
          最近更新 更多