如何在 Flutter 中的计时器页面之间导航,同时保留 AppBar 和 BottomBar?

问题描述 投票:0回答:3

我正在开发一个带有计时器功能的 Flutter 应用程序。目前,当计时器达到零时,我想自动转换到新页面(“shortbreak.dart”),同时保持整体应用程序布局,包括 AppBar 和 BottomBar。

我现有的结构:

  • home_page_timer.dart: 包含主计时器和导航逻辑。
  • startpomodoro.dart:定义首次页面。
  • shortbreak.dart:定义第二个计时器页面。

我有这个设计:

如果时间到了,我想自动导航到这个页面

代码:

home_page_timer.dart

class HomePageTimerUI extends StatelessWidget {
bool PomoRed = false;
bool ShortYellow = false;
bool LongBlue = false;


  @override
  Widget build(BuildContext context) {
    return Container(
        height: 600,
        width: double.infinity,
        child: DefaultTabController(
            length: 3,
            child: Scaffold(
                bottomNavigationBar: BottomBar(),
                appBar: AppBar(
                  elevation: 1.0,
                  backgroundColor: Colors.transparent,
                  bottom: PreferredSize(
                    preferredSize: Size.fromHeight(55),
                    child: Container(
                      color: Colors.transparent,
                      child: SafeArea(
                        child: Column(
                          children: <Widget>[
                            TabBar(
                                indicator: UnderlineTabIndicator(
                                    borderSide: BorderSide(
                                        color: Color(0xff3B3B3B), width: 4.0),
                                    insets: EdgeInsets.fromLTRB(
                                        12.0, 12.0, 12.0, 11.0)),
                                indicatorWeight: 15,
                                indicatorSize: TabBarIndicatorSize.label,
                                labelColor: Color(0xff3B3B3B),
                                labelStyle: TextStyle(
                                    fontSize: 12,
                                    letterSpacing: 1.3,
                                    fontWeight: FontWeight.w500),
                                unselectedLabelColor: Color(0xffD7D7D7),
                                tabs: [
                                  Tab(
                                    text: "POMODORO",
                                    icon: Icon(Icons.work_history, size: 40),
                                  ),
                                  Tab(
                                    text: "SHORT BREAK",
                                    icon: Icon(Icons.ramen_dining, size: 40),
                                  ),
                                  Tab(
                                    text: "LONG BREAK",
                                    icon: Icon(Icons.battery_charging_full_rounded,
                                        size: 40),
                                  ),
                                ])
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
                body: TabBarView(
                  children: <Widget>[
                    Center(
                      child: StartPomodoro(),
                    ),
                    Center(
                      child: ShortBreak(),
                    ),
                    Center(
                        child: LongBreak()
                    ),
                  ],
                ))));

  }
}

startpomodoro.dart

    class StartPomodoro extends StatefulWidget {
      const StartPomodoro({Key? key}) : super(key: key);
    
      @override
      State<StartPomodoro> createState() => _StartPomodoroState ();
    }
    
    class _StartPomodoroState extends State<StartPomodoro>
        with TickerProviderStateMixin {
      List<bool> isSelected = [true, false];
      late Timer timer;
      late AnimationController controller;
    
      String get countText {
        Duration count = controller.duration! * controller.value;
        return controller.isDismissed
            ? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
            : '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
      }
    
      double progress = 1.0;
      bool LongBreak = true;
    
      void notify() {
        if (countText == '00:00:00') {
          FlutterRingtonePlayer.playNotification();
      }
    }
    
      @override
      void initState() {
        super.initState();
        controller = AnimationController(
          vsync: this,
          duration: Duration(seconds: 0),
        );
        controller.addListener(() {
          notify();
          if (controller.isAnimating) {
            setState(() {
              progress = controller.value;
            });
          } else {
            setState(() {
              progress = 1.0;
              LongBreak = true;
            });
          }
        });
      }
    
      @override
      void dispose() {
        controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        ThemeData themeData = Theme.of(context);
        return Scaffold(
          backgroundColor: LongBreak? Color(0xffD94530) : Color(0xff6351c5),
          body: GestureDetector(
            onTap: () {
              if (controller.isDismissed) {
                showModalBottomSheet(
                  context: context,
                  builder: (context) => Container(
                    height:300,
                    child: CupertinoTimerPicker(
                      initialTimerDuration: controller.duration!,
                      onTimerDurationChanged: (time) {
                        setState(() {
                          controller.duration = time;
                        });
                      },
                    ),
                  ),
                );
              }
            },
    
            child: AnimatedBuilder(
                animation: controller,
                builder: (context, child) {
                  return Stack(
                    children: <Widget>[
                      Align(
                        alignment: Alignment.bottomCenter,
                        child: Container(
                          color: Color(0xffD94530),
                          height:
                          controller.value * MediaQuery.of(context).size.height * 0.742,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: <Widget>[
                            Expanded(
                              child: Align(
                                alignment: Alignment.bottomCenter,
                                child:
                                Align(
                                  alignment: FractionalOffset.bottomCenter,
                                  child: Column(
                                    mainAxisAlignment: MainAxisAlignment.end,
                                    crossAxisAlignment: CrossAxisAlignment.center,
                                    children: <Widget>[
                                      Text(
                                        countText,
                                        style: TextStyle(
                                          fontSize: 90.0,
                                          color: Color(0xffF2F2F2),),
                                      ),
                                    ],
                                  ),
                                ),
                              ),
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                AnimatedBuilder(
                                    animation: controller,
                                    builder: (context, child) {
                                      return Padding(
                                        padding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 15.0),
                                      );
                                    }),
                                AnimatedBuilder(
                                    animation: controller,
                                    builder: (context, child) {
                                      return Padding(
                                        padding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 15.0),
                                        child: FloatingActionButton.extended(
                                            backgroundColor: Color(0xffF2F2F2),
                                            onPressed:() {
                                              if (controller.isAnimating) {
                                                controller.stop();
                                                setState(() {
                                                  LongBreak = false;
                                                });
                                              } else {
                                                controller.reverse(
                                                    from: controller.value == 0 ? 1.0 : controller.value);
                                                setState(() {
                                                  LongBreak = false;
                                                });
                                              }
                                            },
                                            icon: Icon(
                                              controller.isAnimating ?
                                              Icons.pause : Icons.play_arrow,
                                              color: Color(0xff3B3B3B),),
                                            label: Text(
                                              controller.isAnimating ?  "PAUSE" : "PLAY",
                                              style: TextStyle(color: Color(0xff3B3B3B)),)),
                                      );
                                    }),
                                SizedBox(width: 20,),
                              ],
                            ),
                          ],
                        ),
                      ),
                    ],
                  );
                }),
          ),
        );
      }
    }
    
    
    

shortbreak.dart

class ShortBreak extends StatefulWidget {
  const ShortBreak({Key? key}) : super(key: key);

  @override
  State<ShortBreak> createState() => _ShortBreakState ();
}

class _ShortBreakState extends State<ShortBreak>
    with TickerProviderStateMixin {
  List<bool> isSelected = [true, false];
  late Timer timer;
  late AnimationController controller;

  String get countText {
    Duration count = controller.duration! * controller.value;
    return controller.isDismissed
        ? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
        : '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
  }

  double progress = 1.0;
  bool LongBreak = true;

  void notify() {
    if (countText == '00:00:00') {
      FlutterRingtonePlayer.playNotification();
    }
  }

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 0),
    );
    controller.addListener(() {
      notify();
      if (controller.isAnimating) {
        setState(() {
          progress = controller.value;
        });
      } else {
        setState(() {
          progress = 1.0;
          LongBreak = true;
        });
      }
    });
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    ThemeData themeData = Theme.of(context);
    return Scaffold(
      backgroundColor: LongBreak? Color(0xff6351c5) : Color(0xffD94530),
      body: GestureDetector(
        onTap: () {
          if (controller.isDismissed) {
            showModalBottomSheet(
              context: context,
              builder: (context) => Container(
                height:300,
                child: CupertinoTimerPicker(
                  initialTimerDuration: controller.duration!,
                  onTimerDurationChanged: (time) {
                    setState(() {
                      controller.duration = time;
                    });
                  },
                ),
              ),
            );
          }
        },

        child: AnimatedBuilder(
            animation: controller,
            builder: (context, child) {
              return Stack(
                children: <Widget>[
                  Align(
                    alignment: Alignment.bottomCenter,
                    child: Container(
                      color: Color(0xff6351c5),
                      height:
                      controller.value * MediaQuery.of(context).size.height * 0.742,
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.all(8.0),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        Expanded(
                          child: Align(
                            alignment: Alignment.bottomCenter,
                            child:
                            Align(
                              alignment: FractionalOffset.bottomCenter,
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.end,
                                crossAxisAlignment: CrossAxisAlignment.center,
                                children: <Widget>[
                                  Text(
                                    countText,
                                    style: TextStyle(
                                      fontSize: 90.0,
                                      color: Color(0xffF2F2F2),),
                                  ),
                                ],
                              ),
                            ),
                          ),
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            AnimatedBuilder(
                                animation: controller,
                                builder: (context, child) {
                                  return Padding(
                                    padding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 15.0),
                                  );
                                }),
                            AnimatedBuilder(
                                animation: controller,
                                builder: (context, child) {
                                  return Padding(
                                    padding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 15.0),
                                    child: FloatingActionButton.extended(
                                        backgroundColor: Color(0xffF2F2F2),
                                        onPressed:() {
                                          if (controller.isAnimating) {
                                            controller.stop();
                                            setState(() {
                                              LongBreak = false;
                                            });
                                          } else {
                                            controller.reverse(
                                                from: controller.value == 0 ? 1.0 : controller.value);
                                            setState(() {
                                              LongBreak = false;

                                            });
                                          }
                                        },
                                        icon: Icon(
                                          controller.isAnimating ?
                                          Icons.pause : Icons.play_arrow,
                                          color: Color(0xff3B3B3B),),
                                        label: Text(
                                          controller.isAnimating ?  "PAUSE" : "PLAY",
                                          style: TextStyle(color: Color(0xff3B3B3B)),)),
                                  );
                                }),
                            SizedBox(width: 20,),
                          ],
                        ),
                      ],
                    ),
                  ),
                ],
              );
            }),
      ),
    );
  }
}

我尝试过的:

我使用

Navigator.pushReplacement
来切换页面,但这会删除我现有的 AppBar 和 BottomBar。

我试过这个:

void notify() {
    if (countText == '00:00:00') {
      FlutterRingtonePlayer.playNotification();
      Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=> 
 StartShortBreak()));
});
    }
  }

目标:

如何在同一应用程序结构中的计时器页面之间无缝导航,同时保持 AppBar 和 BottomBar 一致?

flutter dart navigation state-management appbar
3个回答
1
投票

您想要在选项卡视图之间导航,而不是在实际导航器之间

首先,使用 SigleTickerMixin 将您的 homeui 更改为有状态小部件, 不要使用 DefaultTabController,而是制作一个。 :

late TabController _tabController;
  
  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

并将该选项卡控制器分配给选项卡视图和选项卡栏:

TabBar(
                controller: _tabController,

TabBarView(
            controller: _tabController,

用这个

void notify() {
    if (countText == '00:00:00') {
      FlutterRingtonePlayer.playNotification();
//REMOVE THIS NAVIGATOR
      Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=> 
 StartShortBreak()));
});
//Change to this :
_tabController.animateTo(1, duration: const Duration(milliseconds: 300));
    }
  }

为什么索引是1,因为你把ShortBreak放在你的tabview childreen上的第2位


1
投票

在主页。 Dart 创建一个选项卡控制器并将其分配给选项卡栏视图。然后创建一个改变索引的方法

void chageIndex(int index)
{
  _tabController.animateTo(index);
}

然后将此方法传递到促销屏幕

class StartPomodoro extends StatefulWidget {
final Function (index) changeIndex;
      const StartPomodoro({Key? key, required this.changeIndex}) : super(key: key);
    
      @override
      State<StartPomodoro> createState() => _StartPomodoroState ();
    }

你必须这样通过

body: TabBarView(
                  children: <Widget>[
                    Center(
                      child: StartPomodoro(changeIndex : changeIndex),
                    ),
                    Center(
                      child: ShortBreak(),
                    ),
                    Center(
                        child: LongBreak()
                    ),
                  ],

然后在 startpromodoro 中代替导航器。推一下就可以用了

widget.changeIndex(1);

0
投票

在启动计时器或任何您想做的事情时将其放入您的条件中:

Future.delayed(const Duration(milliseconds: 500), () {
  Navigator.pop(context);
});
© www.soinside.com 2019 - 2024. All rights reserved.