如何在SharedPreferences变更时重建StreamBuilder组件
解决方案
你之前用FutureBuilder包裹的问题在于,它只会在首次获取startDate时构建一次,当用户修改设置后,Future不会重新执行,所以StreamBuilder不会感知到设置变化,自然不会重建列表。要实现设置修改后触发StreamBuilder重建,需要把SharedPreferences的变化转换成可监听的Stream,再和数据库的数据流结合。
方法一:用Stream管理设置变化(推荐)
1. 创建设置管理类
这个类负责封装SharedPreferences的读写,并将设置变化以Stream的形式对外发布:
import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; class SettingsManager { static final SettingsManager _instance = SettingsManager._internal(); factory SettingsManager() => _instance; SettingsManager._internal(); final _startDateController = StreamController<int>.broadcast(); int? _currentStartDate; Stream<int> get startDateStream => _startDateController.stream; Future<void> init() async { final prefs = await SharedPreferences.getInstance(); _currentStartDate = prefs.getInt('startDate') ?? 0; _startDateController.add(_currentStartDate!); } Future<void> updateStartDate(int newDate) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt('startDate', newDate); _currentStartDate = newDate; _startDateController.add(newDate); } void dispose() { _startDateController.close(); } }
2. 修改Overview组件为StatefulWidget
合并设置流和数据库流,让任意一个变化都触发UI重建:
import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; // 替换成你的数据模型 class YourDataModel { final int date; // 其他字段... YourDataModel(this.date); } class Overview extends StatefulWidget { const Overview({super.key}); @override State<Overview> createState() => _OverviewState(); } class _OverviewState extends State<Overview> { late final SettingsManager _settingsManager; late final Stream<List<YourDataModel>> _combinedStream; @override void initState() { super.initState(); _settingsManager = SettingsManager(); _settingsManager.init(); // 合并设置流与数据库流,任意一个更新都会触发builder _combinedStream = CombineLatestStream.combine2( _settingsManager.startDateStream, database.watchAll(), // 你的数据库数据流 (int startDate, List<YourDataModel> data) { // 这里可根据startDate过滤数据,不需要过滤直接返回data即可 return data.where((item) => item.date >= startDate).toList(); }, ); } @override void dispose() { _settingsManager.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( height: 230, margin: const EdgeInsets.all(10), padding: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Theme.of(context).colorScheme.primaryContainer, Theme.of(context).colorScheme.inversePrimary, ], ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), spreadRadius: 2, blurRadius: 8, offset: const Offset(0, 2), ) ], ), child: StreamBuilder<List<YourDataModel>>( stream: _combinedStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return const Center(child: Text('加载出错')); } final data = snapshot.data ?? []; return ListView.builder( itemCount: data.length, itemBuilder: (context, index) { // 替换成你的列表项UI return ListTile(title: Text('数据项 ${data[index].date}')); }, ); }, ), ); } }
3. 修改设置时触发更新
当用户修改startDate时,调用以下代码即可触发StreamBuilder重建:
await SettingsManager().updateStartDate(newValue);
方法二:手动监听状态(无需第三方包)
如果不想引入rxdart,可以在State中分别监听设置和数据库的变化,通过setState触发重建:
class _OverviewState extends State<Overview> { late final SettingsManager _settingsManager; int _startDate = 0; List<YourDataModel> _data = []; @override void initState() { super.initState(); _settingsManager = SettingsManager(); _initSettings(); _listenToDatabase(); } Future<void> _initSettings() async { await _settingsManager.init(); _settingsManager.startDateStream.listen((newDate) { setState(() => _startDate = newDate); }); } void _listenToDatabase() { database.watchAll().listen((newData) { setState(() => _data = newData); }); } @override void dispose() { _settingsManager.dispose(); super.dispose(); } @override Widget build(BuildContext context) { // 根据startDate过滤数据 final filteredData = _data.where((item) => item.date >= _startDate).toList(); return Container( // 装饰部分不变 child: ListView.builder( itemCount: filteredData.length, itemBuilder: (context, index) { return ListTile(title: Text('数据项 ${filteredData[index].date}')); }, ), ); } }
内容的提问来源于stack exchange,提问作者ls.moose
相关产品推荐
相关产品推荐

