Flutter:点击ModalBottomSheet保存按钮后刷新GridView的问题
解决方案:点击底部弹窗保存按钮后刷新GridView
步骤1:将PostsPage改为StatefulWidget
原PostsPage是无状态组件,无法维护排序参数的状态变化,需改为有状态组件,通过内部状态变量控制排序参数,触发页面刷新。
修改后的代码:
class PostsPage extends StatefulWidget { final String num; final String nome; final String initialAtrib; final int startIndex; final int postLimit; PostsPage({ Key? key, required this.num, required this.nome, required this.initialAtrib, required this.postLimit, required this.startIndex, }) : super(key: key); @override State<PostsPage> createState() => _PostsPageState(); } class _PostsPageState extends State<PostsPage> { late String _currentAtrib; @override void initState() { super.initState(); _currentAtrib = widget.initialAtrib; } @override Widget build(BuildContext context) { return BlocProvider( create: (_) => PostBloc( RepositoryProvider.of<PostRepository>(context), widget.num, widget.startIndex, widget.postLimit, _currentAtrib, )..add(PostFetched()), child: Scaffold( appBar: AppBar( title: Text(widget.nome), leading: IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.arrow_back_ios), ), actions: [ Padding( padding: const EdgeInsets.all(10.0), child: FloatingActionButton.extended( onPressed: () async { // 等待底部弹窗返回选中的排序值 final selectedAtrib = await showModalBottomSheet<String>( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), context: context, builder: (context) { return SizedBox( height: 250, child: teste( num: widget.num, startIndex: widget.startIndex, postLimit: widget.postLimit, initialSelected: _currentAtrib, ), ); }, ); // 更新状态触发页面刷新 if (selectedAtrib != null) { setState(() { _currentAtrib = selectedAtrib; }); } }, label: const Text( 'Filtros', style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold), ), backgroundColor: Colors.white, icon: const Icon(Icons.arrow_drop_down_sharp, size: 30, color: Colors.blue), ), ), ], ), body: PostsList( startIndex: widget.startIndex, postLimit: widget.postLimit, num: widget.num, atrib: _currentAtrib, ), ), ); } }
步骤2:修改底部弹窗组件,实现数据返回逻辑
原弹窗点击保存按钮仅创建PostsList实例,需改为关闭弹窗并返回选中的排序值,同时支持接收当前选中的初始值保持状态一致。
修改后的代码:
const List<Widget> opcoes = <Widget>[ Text('Referencia'), Text('Preço'), Text('Nome'), Text('Quantidade'), ]; class teste extends StatefulWidget { final String num; final int startIndex; final int postLimit; final String initialSelected; teste({ Key? key, required this.num, required this.startIndex, required this.postLimit, required this.initialSelected, }) : super(key: key); @override State<teste> createState() => _testeState(); } class _testeState extends State<teste> { late List<bool> _selectedOpcoes; final List<String> _selectedTexto = <String>[ 'Reference', 'Price', 'Name', 'Quantity' ]; late String finalTexto; @override void initState() { super.initState(); // 根据初始值设置选中状态 final initialIndex = _selectedTexto.indexOf(widget.initialSelected); _selectedOpcoes = List.filled(4, false); if (initialIndex != -1) { _selectedOpcoes[initialIndex] = true; finalTexto = _selectedTexto[initialIndex]; } else { _selectedOpcoes[0] = true; finalTexto = _selectedTexto[0]; } } @override Widget build(BuildContext context) { return ListView( children: [ Padding( padding: const EdgeInsets.only(bottom: 10, left: 270, right: 10, top: 10), child: ElevatedButton( onPressed: () { // 返回选中值并关闭弹窗 Navigator.pop(context, finalTexto); }, child: const Text('Guardar', style: TextStyle(fontSize: 18)), ), ), const SizedBox(height: 70), const Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: Text( 'Ordenar por:', style: TextStyle(fontSize: 18, color: Colors.black, fontWeight: FontWeight.bold), ), ), const SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 30), child: ToggleButtons( direction: Axis.horizontal, onPressed: (int index) { setState(() { for (int i = 0; i < _selectedOpcoes.length; i++) { _selectedOpcoes[i] = i == index; } finalTexto = _selectedTexto[index]; }); }, borderRadius: const BorderRadius.all(Radius.circular(8)), selectedBorderColor: primeColor, selectedColor: primeColor, fillColor: Colors.white, color: Colors.black, constraints: const BoxConstraints(minHeight: 40.0, minWidth: 80.0), isSelected: _selectedOpcoes, children: opcoes, ), ), ], ); } }
步骤3:优化PostsList组件,确保参数变化时重新加载数据
移除State构造函数传递参数的方式,直接使用widget.xxx访问参数,避免状态不一致;当排序参数变化时,Bloc会重新初始化并触发数据获取事件,刷新GridView内容。
修改后的代码:
class PostsList extends StatefulWidget { final String num; final int startIndex; final int postLimit; final String atrib; PostsList({ Key? key, required this.num, required this.startIndex, required this.postLimit, required this.atrib, }) : super(key: key); @override State<PostsList> createState() => _PostsListState(); } class _PostsListState extends State<PostsList> { final _scrollController = ScrollController(); @override void initState() { super.initState(); _scrollController.addListener(_onScroll); } @override Widget build(BuildContext context) { return BlocProvider( create: (_) => PostBloc( RepositoryProvider.of<PostRepository>(context), widget.num, widget.startIndex, widget.postLimit, widget.atrib, )..add(PostFetched()), child: BlocBuilder<PostBloc, PostState>( builder: (context, state) { switch (state.status) { case PostStatus.failure: return const Center(child: Text('Falha ao carregar produtos')); case PostStatus.success: if (state.posts.isEmpty) { return const Center(child: Text('Lista de produtos vazia')); } return Column( children: [ Expanded( child: Container( padding: const EdgeInsets.fromLTRB(3, 0, 0, 0), color: Colors.grey[350], child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 5, crossAxisSpacing: 5, childAspectRatio: 0.55, ), itemCount: state.hasReachedMax ? state.posts.length : state.posts.length + 2, controller: _scrollController, itemBuilder: (BuildContext context, int index) { if (index >= state.posts.length) { return const BottomLoader(); } final post = state.posts[index]; return post.quantity > 0 ? PostListItemdisp(post: post) : PostListItemIndisp(post: post); }, ), ), ), ], ); case PostStatus.initial: return const Center(child: CircularProgressIndicator()); } }, ), ); } @override void dispose() { _scrollController ..removeListener(_onScroll) ..dispose(); super.dispose(); } void _onScroll() { if (_isBottom) { context.read<PostBloc>().add(PostFetched()); } } bool get _isBottom { if (!_scrollController.hasClients) return false; final maxScroll = _scrollController.position.maxScrollExtent; final currentScroll = _scrollController.offset; return currentScroll >= (maxScroll * 0.8); } }
核心逻辑说明
- 状态驱动刷新:通过PostsPage的状态变量
_currentAtrib控制排序参数,调用setState触发页面重建,PostsList将接收新参数并重新加载数据。 - 弹窗数据传递:使用
await showModalBottomSheet等待弹窗返回选中值,避免直接创建组件,而是通过状态更新实现页面刷新。 - Bloc重新初始化:每次PostsList的排序参数变化时,Bloc会重新创建并触发
PostFetched事件,获取排序后的最新数据。
内容的提问来源于stack exchange,提问作者Rafael Reis
相关产品推荐
相关产品推荐

