【问题标题】:Replace widgets like fragments in Flutter替换 Flutter 中的片段等小部件
【发布时间】:2018-11-11 09:42:16
【问题描述】:

我是 Flutter 的新手。

我有一个带有 2 个子小部件(Android 中的 2 个片段)的应用程序,当我单击 WidgetA 中的下一个按钮时,我想将该小部件替换(或推送)到 WidgetChildA,就像在 Android 中推送(或替换)片段一样。但取而代之的是,我得到了一个全屏小部件,就像 Flutter 中的普通屏幕一样。

这是我的代码:

import 'package:flutter/material.dart';

class DemoFragment extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new DemoFragmentState();
  }
}

class DemoFragmentState extends State<DemoFragment> {
  @override
  Widget build(BuildContext context) {
    print(context.toString() + context.hashCode.toString());
    return new Scaffold(
      appBar: new AppBar(title: new Text("Demo fragment")),
      body: new Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          new FragmentA(),
          new FragmentB()
        ],
      ),
    );
  }
}

class FragmentA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print(context.toString() + context.hashCode.toString());
    return new Center(
      child: new Column(
        children: <Widget>[
          new Text("Fragment A"),
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                print(context.toString() + context.hashCode.toString());
                Navigator.of(context).push(new PageRouteBuilder(
                    opaque: true,
                    transitionDuration: const Duration(milliseconds: 0),
                    pageBuilder: (BuildContext context, _, __) {
                      return new FragmentChildA();
                    }));

                /*showDialog(
                    context: context,
                    builder: (_) => new AlertDialog(
                          title: new Text("Hello world"),
                          content: new Text("this is my content"),
                        ));*/
              })
        ],
      ),
    );
  }
}

class FragmentB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print(context.toString() + context.hashCode.toString());
    return new Center(
        child: new Column(
      children: <Widget>[
        new Text("Fragment B"),
        new RaisedButton(
            child: new Text("next"),
            onPressed: () {
              print(context.toString() + context.hashCode.toString());
              Navigator.of(context).push(new PageRouteBuilder(
                  opaque: true,
                  transitionDuration: const Duration(milliseconds: 0),
                  pageBuilder: (BuildContext context, _, __) {
                    return new FragmentChildB();
                  }));
            })
      ],
    ));
  }
}

class FragmentChildA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
            child: new Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[new Text("Fragment Child A")],
    )));
  }
}

class FragmentChildB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
            child: new Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[new Text("Fragment Child B")],
    )));
  }
}

截图:

Home page

After clicked

【问题讨论】:

  • 你想替换哪里?你想用你的片段替换按钮吗?
  • 我想将 FragmentA 替换为 FragmentChildA
  • 我也想做这个,我有一些想法,我真的不希望整个事情都是有状态的,并在有条件的情况下交换小部件,我想知道底部导航视图是如何做到的,做您对此有任何更新
  • @ChâuMinhPhúc 你能解决你的问题吗?
  • @ChâuMinhPhúc 您找到解决方案了吗?

标签: android dart flutter


【解决方案1】:

我不确定你是否可以使用路由器来替换视图的一部分;但您可以有条件地更改您在 build 方法中呈现的 Widget,如下所示:

children: <Widget>[
    someCondition ? new FragmentA() : new FragmentChildA(),
    new FragmentB()
],

那么你只需要在有状态的小部件中使用setState 来设置someCondition

setState(() => someCondition = true);

如果你想从FragmentA 内部执行此操作,你可以允许它将函数传递给它的构造函数:

new FragmentA(
  onPress: setState(() => someCondition = true)
)

但是,最好将所有这些逻辑封装在一个小部件中,这样该逻辑就不会全部挂在父级中。您可以为 FragmentA 创建一个 StatefulWidget,它会跟踪您所处的阶段,然后在其构建方法中呈现正确的子小部件,例如:

build() {
  switch(stage) {
    Stages.Stage1:
      return new Stage1(
        onNext: () => setState(() => stage = Stages.Stage2);
      );
    Stages.Stage2:
      return new Stage1(
        onPrevious: () => setState(() => stage = Stages.Stage1);
      );
  }
}

【讨论】:

  • 是的,Flutter的这个状态概念我理解了,但是不知道有没有类似Android的方式。
  • 我不熟悉 Android 开发,所以不确定它是如何工作的,但我认为你不能只替换 Flutter 中的小部件的一部分,因为当父构建方法是调用,它只是要再次创建旧的小部件。
  • @Danny Tuppeny 你能帮我解决这个问题吗? https://stackoverflow.com/q/56223550/1830228
【解决方案2】:

您可以简单地使用带有 CupertinoPageTransitionsBuilder 的 MaterialApp 小部件作为 pageTransitionTheme,例如

MaterialApp(
   theme: ThemeData(
   pageTransitionsTheme: PageTransitionsTheme(builders: {
   TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
   TargetPlatform.android: SlideRightPageTransitionsBuilder(),
   }),
   initialRoute: "fragment1",
   routes: <String, WidgetBuilder>{
      "fragment1": (BuildContext context) => Fragment1(),
      "fragment2": (BuildContext context) => Fragment2(),
   }
...
),

然后在片段 1 中,您只需使用以下内容通过幻灯片动画导航到另一个片段

Navigator.of(context).pushReplacementNamed("fragment2");

【讨论】:

    【解决方案3】:

    好的,我将按照与谷歌底部导航栏相同的方式执行此操作,我不认为这是性能最高的,但它可以工作

      class MainFabContainer extends StatefulWidget {
    
        MainFabContainer({
          Key key,
        }) : super(key: key);
    
        @override
        State<StatefulWidget> createState() {
          return MainFabContainerState();
        }
    
      }
    
      class MainFabContainerState extends State<MainFabContainer> {
        String title = "Title";
        int _currentIndex = 0;
        final List<int> _backstack = [0];
    
        @override
        Widget build(BuildContext context) {
          //each fragment is just a widget which we pass the navigate function
          List<Widget> _fragments =[Fragment1(navigate: navigateTo),Fragment2(navigate: navigateTo,),Fragment3(navigate: navigateTo,)];
          //will pop scope catches the back button presses
          return WillPopScope(
            onWillPop: () {
              customPop(context);
            },
            child: Scaffold(
              drawer: drawer(),
              appBar: AppBar(
                title: Text(title),
              ),
              body: Column(
                children: <Widget>[
                  Expanded(
                    child: _fragments[_currentIndex],
                  ),
                ],
              ),
            ),
          );
        }
    
    
        void navigateTo(int index) {
          _backstack.add(index);
          setState(() {
            _currentIndex = index;
          });
        }
    
        void navigateBack(int index) {
          setState(() {
            _currentIndex = index;
          });
        }
    
        customPop(BuildContext context) {
          if (_backstack.length - 1  > 0) {
            navigateBack(_backstack[_backstack.length - 1]);
          } else {
            _backstack.removeAt(_backstack.length - 1);
            Navigator.pop(context);
          }
        }
        //this method could be called by the navigate and navigate back methods
        _setTitle(String appBarTitle) {
          setState(() {
            title = appBarTitle;
          });
        }
      }
    

    【讨论】:

    • whats navigateTo 在片段参数中,当我按下返回按钮时,我得到 Unhandled Exception: Scaffold.geometryOf() must only be accessed during the paint phase. 错误
    • 它在我的回答中,您的错误听起来像是您试图更改错误的小部件
    • 你能说片段参数中的navigateTo参数是什么吗?以及如何使用它们
    • 它在答案中,我认为您需要滚动答案,它显示了 2 个方法 navigateTo 和 navigateBack void navigateTo(int index) { _backstack.add(index); setState(() { _currentIndex = index; }); } void navigateBack(int index) { setState(() { _currentIndex = index; }); }
    • 是的,我知道,请让我创建新问题并链接你,谢谢
    【解决方案4】:

    嗯,我找了几个月处理这个案子的方法,但我只是忘了回答这个问题。

    解决方案是用一个新的导航器包装你的小部件。

    你可以看视频示例here

    还有它的简单演示here

    这个解决方案的缺点是有时,键盘没有按我的意图显示。

    【讨论】:

    • id 喜欢建议将代码解决方案放在 SO 上。而不是仅链接到外部资源。
    • 视频链接坏了):
    猜你喜欢
    • 2021-10-28
    • 1970-01-01
    • 1970-01-01
    • 2022-09-22
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 2015-10-12
    • 2012-04-21
    相关资源
    最近更新 更多