Flutter主页面按钮状态未及时更新问题求助
问题
我有两个页面main.dart和second_page.dart,用Provider类(provider_class.dart)存储共享变量,Button_Data.dart定义按钮数据。当前问题:首次进入第二页完成操作后,点击「返回上一页」按钮回到主页面时,「Sell and Collect」按钮没有立即变为启用状态;必须第二次进入第二页再返回,该按钮才会启用。
需求:无论第二页是否访问过,从第二页返回主页面时,「Sell and Collect」按钮状态都能立即更新为启用状态。
main.dart
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../Classes/provider_class.dart';// Import the provider class import '../Classes/Button_Data.dart'; import 'second_page.dart'; void main() { runApp( ChangeNotifierProvider( create: (context) => PageProvider(), child: MaterialApp( home: MainPage(), ), ), ); } class MainPage extends StatelessWidget { @override Widget build(BuildContext context) { // Get the PageProvider instance from the Provider PageProvider pageProvider = Provider.of<PageProvider>(context); // Now you can access pageProvider.firstPageActiveButtons[1] and other properties final List<Button_Data> buttonsData = [ Button_Data('Check Inventory', pageProvider.firstPageActiveButtons[0]), Button_Data('Sell and Collect', pageProvider.firstPageActiveButtons[1]), Button_Data('Distribute POSM', pageProvider.firstPageActiveButtons[2]), Button_Data('Remit Sales', pageProvider.firstPageActiveButtons[3]), Button_Data('Return and Restock', pageProvider.firstPageActiveButtons[4]), Button_Data('Get ending Inventory', pageProvider.firstPageActiveButtons[5]), ]; return Scaffold( appBar: AppBar( title: Text('EX Trucker Tasks'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: buttonsData.map((buttonData) => CustomButton( key: PageStorageKey<String>(buttonData.name), buttonData: buttonData, onTap: () { if (buttonData.name == 'Check Inventory') { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => SecondPage()), ); } else { print('Button ${buttonData.name} pressed!'); } }, )).toList(), ), ), ); } } class CustomButton extends StatelessWidget { final Button_Data buttonData; final VoidCallback onTap; CustomButton({Key? key, required this.buttonData, required this.onTap}) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.symmetric(vertical: 10), child: ElevatedButton( onPressed: buttonData.isEnabled ? onTap : null, style: ButtonStyle( backgroundColor: buttonData.isEnabled ? null : MaterialStateProperty.all<Color>(Colors.grey), ), child: Text(buttonData.name), ), ); } }
second_page.dart
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../Classes/Button_Data.dart'; import 'main.dart'; import 'package:provider/provider.dart'; import '../Classes/provider_class.dart'; // Import the provider class class SecondPage extends StatefulWidget { @override _SecondPageState createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> with AutomaticKeepAliveClientMixin<SecondPage> { late List<Button_Data> secondPageButtonsData; bool showReturnButton = false; @override bool get wantKeepAlive => true; @override void initState() { super.initState(); initializeSecondPage(); } bool isFirstLoad = true; Future<void> initializeSecondPage() async { PageProvider pageProvider = Provider.of<PageProvider>(context, listen: false); await loadStoredState(); // Enable buttons up to the active index when the page is loaded for the first time if (isFirstLoad) { for (int i = 0; i < secondPageButtonsData.length; i++) { secondPageButtonsData[i].isEnabled = i <= pageProvider.secondPageActiveButtonIndex; } showReturnButton = pageProvider.secondPageActiveButtonIndex == secondPageButtonsData.length - 1; isFirstLoad = false; print('isFirstLoad is true'); } else { print('isFirstLoad is false'); } } Future<void> loadStoredState() async { secondPageButtonsData = [ Button_Data('Task 1', true), Button_Data('Task 2', false), Button_Data('Task 3', false), Button_Data('Task 4', false), Button_Data('Task 5', false), ]; final prefs = await SharedPreferences.getInstance(); for (int i = 0; i < secondPageButtonsData.length; i++) { Map<String, dynamic>? storedData = await retrieveStoredState(secondPageButtonsData[i].name); if (storedData != null) { secondPageButtonsData[i].isEnabled = storedData['isEnabled'] ?? false; } } showReturnButton = secondPageButtonsData.last.isEnabled; } void storeState(Button_Data buttonData) async { final prefs = await SharedPreferences.getInstance(); prefs.setBool('${buttonData.name}_isEnabled', buttonData.isEnabled); } Future<Map<String, dynamic>?> retrieveStoredState(String buttonName) async { final prefs = await SharedPreferences.getInstance(); bool? isEnabled = prefs.getBool('${buttonName}_isEnabled'); if (isEnabled != null) { return {'isEnabled': isEnabled}; } else { return null; } } @override Widget build(BuildContext context) { super.build(context); PageProvider pageProvider = Provider.of<PageProvider>(context); return Scaffold( appBar: AppBar( title: Text('Check Inventory'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ for (int i = 0; i < secondPageButtonsData.length && pageProvider.isFirstLoad; i++) Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (i == pageProvider.secondPageActiveButtonIndex && !showReturnButton) Container( margin: EdgeInsets.only(right: 8.0), width: 8.0, height: 8.0, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.red, ), ), CustomButton( buttonData: secondPageButtonsData[i], onTap: () { setState(() { if (i == pageProvider.secondPageActiveButtonIndex && i < secondPageButtonsData.length - 1) { // Check if the correct button is pressed to increment the index pageProvider.setSecondPageActiveButtonIndex(i + 1); secondPageButtonsData[pageProvider.secondPageActiveButtonIndex].isEnabled = true; storeState(secondPageButtonsData[pageProvider.secondPageActiveButtonIndex]); // Change the text on the button to "Task completed" secondPageButtonsData[i].name = 'Task ${pageProvider.secondPageActiveButtonIndex} completed'; } else if (i == secondPageButtonsData.length - 1) { // Show 'Return to previous page' button when 'Task 5' is pressed secondPageButtonsData[i].name = 'Task 5 completed'; showReturnButton = true; } }); }, ), ], ), // Other widgets go here if (!pageProvider.isFirstLoad) // Show the Text widgets when isFirstLoad is false ...[ Text('Task 1 complete', style: TextStyle(fontSize: 20)), Text('Task 2 complete', style: TextStyle(fontSize: 20)), Text('Task 3 complete', style: TextStyle(fontSize: 20)), Text('Task 4 complete', style: TextStyle(fontSize: 20)), Text('Task 5 complete', style: TextStyle(fontSize: 20)), ], // 'Return to previous page' button if ((showReturnButton || !pageProvider.isFirstLoad) ) ElevatedButton( onPressed: () async { await Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => MainPage(), ), ); // Set isFirstLoad to false and notify listeners pageProvider.setIsFirstLoad(false); pageProvider.firstPageActiveButtons[1] = true; }, child: Text('Return to previous page'), ), ], ), ), ); } }
provider_class.dart
import 'package:flutter/material.dart'; class PageProvider extends ChangeNotifier { int secondPageActiveButtonIndex = 0; int mainPageNoticeIndex = 0; // Declare a fixed-length list with a specified size and default values List<bool> firstPageActiveButtons = List<bool>.filled(6, false); bool isFirstLoad = true; // Added variable to track the first load PageProvider() { // Initialize the list or set its values in the constructor firstPageActiveButtons[0] = true; firstPageActiveButtons[1] = false; firstPageActiveButtons[2] = false; firstPageActiveButtons[3] = false; firstPageActiveButtons[4] = false; firstPageActiveButtons[5] = false; } void setSecondPageActiveButtonIndex(int index) { secondPageActiveButtonIndex = index; notifyListeners(); } void setIsFirstLoad(bool value) { isFirstLoad = value; // Update the value of isFirstLoad notifyListeners(); } }
Button_Data.dart
class Button_Data { String name; bool isEnabled; Button_Data(this.name, this.isEnabled); }
解决方案
问题核心有两点:
- 返回主页面时修改Provider状态的代码放在了
Navigator.pushReplacement之后,此时SecondPage已被销毁,Provider的修改无法触发监听更新。 - 修改
firstPageActiveButtons[1]时未调用notifyListeners(),即使值改变,MainPage也收不到状态变化通知。
步骤1:扩展Provider类的方法
在provider_class.dart中新增专门的方法更新按钮状态并触发通知:
// 在PageProvider类中添加 void enableSellAndCollectButton() { firstPageActiveButtons[1] = true; notifyListeners(); // 触发所有监听Widget更新 }
步骤2:调整返回按钮的逻辑顺序
在second_page.dart的返回按钮点击事件中,先更新Provider状态,再执行页面跳转:
ElevatedButton( onPressed: () async { // 先更新Provider状态,再跳转页面 PageProvider pageProvider = Provider.of<PageProvider>(context, listen: false); pageProvider.setIsFirstLoad(false); pageProvider.enableSellAndCollectButton(); // 调用新增方法 await Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => MainPage()), ); }, child: Text('Return to previous page'), ),
效果验证
修改后,用户点击返回按钮时,会先更新Provider中的按钮状态并触发通知,再跳转到主页面。MainPage因监听了Provider变化会自动重建,「Sell and Collect」按钮会立即读取到最新的true值,变为启用状态。
内容的提问来源于stack exchange,提问作者Placido Penitente
相关产品推荐
相关产品推荐

