【问题标题】:Update Scaffold-Properties in nested CupertinoPageScaffold in Flutter更新 Flutter 中嵌套 CupertinoPageScaffold 中的 Scaffold-Properties
【发布时间】:2021-11-24 09:05:09
【问题描述】:

如何在子窗口小部件(页面)中更新根脚手架的某些属性。

这是我的根脚手架中的一个 sn-p。

CupertinoPageScaffold(
      resizeToAvoidBottomInset:
          state.resizeToAvoidBottomInsets, //update this here
      child: CupertinoTabScaffold(
          controller: _tabController,
          tabBar: CupertinoTabBar(
            onTap: onTap,
            items: widget.items,
          ),
          tabBuilder: (BuildContext context, index) {
            return StatusBarPadding(child: _tabs[index]);
          }),
    ),

docs 说,我应该添加一个侦听器以避免嵌套脚手架(例如更新 resizeToAvoidBottomInset)。 但是,这仅适用于每个选项卡的一页。当我嵌套选项卡时,我无法再直接访问它们。

我尝试了两种解决方案,我将在下面解释(+问题):

解决方案 1:提供者

我使用 Provider 来跟踪全局导航栏状态:

class NavbarState extends ChangeNotifier {
  bool _resizeBottom;

  NavbarState(this._resizeBottom);

  get resizeBottom => _resizeBottom;

  void setResizeBottom(bool state) {
    _resizeBottom = state;
    notifyListeners();
  }
}

然后在我的页面中,我在 initState-Method 中使用BlocProvider.of<NavbarState>(context).setResizeBottom(val)(分别为 dispose)设置状态。

这有2个问题

  1. 调用notifyListeners会在consumer中触发一个setState,不能在initState方法中调用setState。

  2. 我必须在每个 initState 和 dispose 方法中声明它。

解决方案 2:集团

我再一次拥有了一个全局状态,但它不必继​​承自 `ChangeNotifier`。我使用 `NavbarBloc` 类跟踪状态。

然后我可以在onGenerateRoute 方法中添加一个事件。这比提供者方法更方便,因为我只在一个地方管理这个状态。

但是,还有一个很大的问题

当我返回导航时,onGenerateRoute 方法没有被调用,因此状态没有得到更新。

最简单的解决方案是什么

至少从应用程序开发人员的角度来看,如果我可以只询问位于活动导航器中的当前小部件,那就太好了。

导航栏示例

这是给定 cupertinotabscaffold 的 3 个导航器的图示。

中间的“堆栈”处于活动状态,屏幕上可以看到最顶部的小部件。因此,目前 resize 参数应该是错误的。在堆栈之间导航(点击导航图标),调整大小参数应该调整。此外,在单个堆栈(push、pop)之间导航时,还应调整调整大小参数(例如,在弹出时,参数应设置为 true)。

我找不到类似的东西。所以我需要你的帮助。

【问题讨论】:

    标签: flutter flutter-navigation flutter-cupertino


    【解决方案1】:

    对于设置状态,设置器作为onTap 的回调如何?

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return CupertinoApp(
          title: 'Flutter Demo',
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key? key}) : super(key: key);
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      var values = [
        [true, false],
        [false, true],
      ];
    
      int stackIndex = 0;
      bool _resizeToAvoidBottomInsets = true;
      set _resizeToAvoidButtomInsets(bool value) => setState(() {
            print("set _resizeToAvoidBottomInsets = $value");
            _resizeToAvoidBottomInsets = value;
          });
    
      void handleTap(int i) {
        print("tapped: $i");
        _resizeToAvoidBottomInsets = values[stackIndex][i];
      }
    
      @override
      Widget build(BuildContext context) {
        return CupertinoPageScaffold(
          resizeToAvoidBottomInset: _resizeToAvoidBottomInsets,
          child: CupertinoTabScaffold(
            tabBar: CupertinoTabBar(
              items: const <BottomNavigationBarItem>[
                BottomNavigationBarItem(icon: Icon(Icons.ac_unit)),
                BottomNavigationBarItem(icon: Icon(Icons.wb_sunny)),
              ],
              onTap: handleTap,
            ),
            tabBuilder: (BuildContext context, int index) {
              return CupertinoTabView(
                builder: (BuildContext context) {
                  return CupertinoPageScaffold(
                    navigationBar: CupertinoNavigationBar(
                      middle: Text('Page 1 of tab $index'),
                    ),
                    child: Center(
                      child: CupertinoButton(
                        child: Text(
                          'resizeToAvoidBottomInsets: $_resizeToAvoidBottomInsets',
                        ),
                        onPressed: () {
                          // set state and increment stack index before push
                          stackIndex++;
                          _resizeToAvoidButtomInsets = values[stackIndex][0];
                          Navigator.of(context).push(
                            CupertinoPageRoute<void>(
                              builder: (BuildContext context) {
                                return CupertinoPageScaffold(
                                  navigationBar: CupertinoNavigationBar(
                                    middle: Text('Page 2 of tab $index'),
                                  ),
                                  child: Center(
                                    child: CupertinoButton(
                                        child: Text(
                                          'resizeToAvoidBottomInsets: $_resizeToAvoidBottomInsets',
                                        ),
                                        onPressed: () {
                                          // set state and decrement stack index before pop
                                          stackIndex--;
                                          _resizeToAvoidButtomInsets =
                                              values[stackIndex][0];
                                          Navigator.of(context).pop();
                                        }),
                                  ),
                                );
                              },
                            ),
                          );
                        },
                      ),
                    ),
                  );
                },
              );
            },
          ),
        );
      }
    }
    
    

    【讨论】:

    • 这并不能完全解决问题。因为它仅在您在导航图标之间切换时更改调整大小参数。我调整了我的问题以说明 navigator-stacks 的示例配置。
    • 我已根据您的编辑编辑了我的答案。让我知道这是否达到了预期的效果。
    • 感谢您的努力 :) 问题仍然存在。在您的示例中,您只有两个页面,并且只能通过按下按钮进行导航。 1. 假设你有一个更大的项目,那么你必须修改每一页(大约 50 页)。 2. 您忽略了这样一个事实,即您也可以通过 swipeBack(iOS)/navBack(Android) 导航并且无法控制。您可以使用 WillPopScope 包装每个页面,但 1. 仍然存在。
    【解决方案2】:

    所以我针对给定的问题实施了一个解决方案。我认为这不是很顺利,但至少可以从每个页面中抽象出脚手架属性。

    如 question-solution-try-2 中已经提到的那样,我使用了一个集团。

    该集团发出一个状态MyScaffoldState,其中包含一个ScaffoldProperties 属性。对于这个玩具示例,我将其实现如下:

    class ScaffoldProperties {
      bool resizeBottom;
      String title;
    
      ScaffoldProperties({this.resizeBottom = false, required this.title});
    
      ScaffoldProperties copyWith({ScaffoldProperties? state}) {
        return ScaffoldProperties(
          resizeBottom: state?.resizeBottom ?? resizeBottom,
          title: state?.title ?? title,
        );
      }
    }
    

    您可以将任何脚手架属性添加到此类。

    主要工作发生在我的MultiTabScaffold 班级。这个类接受参数

    List<Widget> pages;
    List<BottomNavigationBarItem> items;
    Route<dynamic>? Function(RouteSettings)? onGenerateRoute;
    List<NavigatorObserver> navigatorObservers
    

    最重要的是

    Function<ScaffoldProperties> getScaffoldPropertiesFromRouteName(String? route);
    

    该类将必要的 Scaffolds 包裹在页面周围。此外,它使用BlocBuilder,因此可以使用当前状态的ScaffoldProperties

    getScaffoldPropertiesFromRouteName 方法采用路由并返回 ScaffoldProperties-Instance。因此,所有标题、resizeProperties、...都必须在这里收集。

    要更新当前状态(或向集团发出事件),我必须修改一些点。

    1. onGenerateRoute:生成路由后(推送到导航器),必须发出当前页面的ScaffoldProperties(使用getScaffoldPropertiesFromRouteName函数)
    2. WillPopScope:同样在弹出页面后,我会发出一个带有下面页面属性的事件。
    3. 初始值:最初,当您打开应用程序时,不会调用 onGenerateRoute 和 willpop。因此,您必须对每个 Navigator 的初始路由“/”进行不同的处理。

    我有一个完全实现的玩具example

    对于这样一个简单的用例来说,这感觉需要做很多工作,但我找不到更简单的方法来做到这一点。如果有,请告诉我。

    【讨论】:

      猜你喜欢
      • 2021-02-13
      • 2022-11-28
      • 2020-04-10
      • 2023-04-03
      • 2021-07-22
      • 1970-01-01
      • 2021-07-26
      • 2021-12-14
      • 2014-05-17
      相关资源
      最近更新 更多