无法从其他Widget调用Widget,但可从Main调用的问题排查
问题描述
我正在开发一个简易计时器应用,通过滑块设置计时时长,点击「开始」按钮启动倒计时,点击「停止」按钮重置并停止计时。拆分了三个组件:timer_model(继承自ChangeNotifier)、timer_control和timer_view,后两者都通过Consumer<TimerModel>监听状态变化。
遇到的异常情况:
- 将
main函数中home设为TimerControl时,代码可正常调试运行 - 设为
TimerView时直接崩溃,无法调试 - 注释掉
TimerView中的TimerControl组件后,代码又能正常查看和调试,不清楚问题根源
代码示例
主文件
import 'package:controller_ui/models/timer_model.dart'; import 'package:flutter/material.dart'; import 'views/timer_view.dart'; import 'package:provider/provider.dart'; void main() { runApp(ChangeNotifierProvider( create: (context) => TimerModel(), child: const MyApp(), )); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: TimerView(), ); } }
Model层(timer_model.dart)
import 'dart:async'; import 'package:flutter/material.dart'; class TimerModel extends ChangeNotifier { Timer? timer; double userSetTime = 10; final int maxTimeSeconds = 180; int timerSetTime = 10; void setTime(int seconds) { userSetTime = seconds.toDouble(); notifyListeners(); } void startCountdown() { if (timer != null && timer!.isActive) return; timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (timerSetTime > 0) { timerSetTime--; } else { timer.cancel(); } }); notifyListeners(); } void resetCountdown() { timerSetTime = userSetTime.round(); // reset to initial value or any desired value timer?.cancel(); notifyListeners(); } }
Control组件(timer_control.dart)
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/timer_model.dart'; class TimerControl extends StatefulWidget { const TimerControl({super.key}); @override TimerControlState createState() => TimerControlState(); } class TimerControlState extends State<TimerControl> { @override Widget build(BuildContext context) { return Consumer<TimerModel>( builder: (context, value, child) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( radius: 100, backgroundColor: const Color.fromARGB(255, 1, 12, 61), child: Text( value.userSetTime.round().toString(), style: const TextStyle(fontSize: 40, color: Colors.white), ), ), Slider( value: value.userSetTime, max: value.maxTimeSeconds.toDouble(), label: value.userSetTime.round().toString(), onChanged: (double seconds) { value.setTime(seconds.round()); }, ), ], ), ), ); }, ); } }
View组件(timer_view.dart)
import 'package:flutter/material.dart'; import '../widgets/timer_control.dart'; import 'package:provider/provider.dart'; import '../models/timer_model.dart'; class TimerView extends StatefulWidget { const TimerView({super.key}); @override State<TimerView> createState() => _TimerViewState(); } class _TimerViewState extends State<TimerView> { @override Widget build(BuildContext context) { return Consumer<TimerModel>( builder: (context, value, child) => Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TimerControl(), const SizedBox( width: 200, ), Column( children: [ ElevatedButton( onPressed: value.startCountdown, child: const Text('Start')), ElevatedButton( onPressed: value.resetCountdown, child: const Text('Stop')), ], ) ], ), )); } }
问题根源与修复方案
核心问题
- 嵌套Scaffold冲突:
TimerControl自身返回了一个Scaffold,而TimerView也返回了一个Scaffold,嵌套Scaffold会导致Flutter布局结构混乱,触发渲染异常。 - 冗余Consumer监听:
TimerView已经通过Consumer获取了TimerModel,TimerControl内部重复使用Consumer属于冗余操作,且可能引发上下文监听冲突。
修复步骤
1. 修改TimerControl组件
移除内部的Scaffold和Consumer,改为直接从上下文获取TimerModel:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/timer_model.dart'; class TimerControl extends StatelessWidget { const TimerControl({super.key}); @override Widget build(BuildContext context) { final timerModel = Provider.of<TimerModel>(context); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( radius: 100, backgroundColor: const Color.fromARGB(255, 1, 12, 61), child: Text( timerModel.userSetTime.round().toString(), style: const TextStyle(fontSize: 40, color: Colors.white), ), ), Slider( value: timerModel.userSetTime, max: timerModel.maxTimeSeconds.toDouble(), label: timerModel.userSetTime.round().toString(), onChanged: (double seconds) { timerModel.setTime(seconds.round()); }, ), ], ), ); } }
2. 修正TimerView布局细节
把垂直布局中的水平间距SizedBox(width:200)改为垂直间距SizedBox(height:40),避免布局错位:
import 'package:flutter/material.dart'; import '../widgets/timer_control.dart'; import 'package:provider/provider.dart'; import '../models/timer_model.dart'; class TimerView extends StatefulWidget { const TimerView({super.key}); @override State<TimerView> createState() => _TimerViewState(); } class _TimerViewState extends State<TimerView> { @override Widget build(BuildContext context) { return Consumer<TimerModel>( builder: (context, value, child) => Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TimerControl(), const SizedBox(height: 40), Column( children: [ ElevatedButton( onPressed: value.startCountdown, child: const Text('Start')), const SizedBox(height: 10), ElevatedButton( onPressed: value.resetCountdown, child: const Text('Stop')), ], ) ], ), )); } }
3. 修复Model层倒计时逻辑
startCountdown方法缺少启动时同步最新设置时间的逻辑,且每秒倒计时时未通知UI更新,补充后代码:
void startCountdown() { if (timer != null && timer!.isActive) return; // 启动前同步最新设置的时间 timerSetTime = userSetTime.round(); timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (timerSetTime > 0) { timerSetTime--; notifyListeners(); // 每秒更新时通知UI } else { timer.cancel(); } }); notifyListeners(); }
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

