You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
  }
}

核心逻辑说明

  1. 状态驱动刷新:通过PostsPage的状态变量_currentAtrib控制排序参数,调用setState触发页面重建,PostsList将接收新参数并重新加载数据。
  2. 弹窗数据传递:使用await showModalBottomSheet等待弹窗返回选中值,避免直接创建组件,而是通过状态更新实现页面刷新。
  3. Bloc重新初始化:每次PostsList的排序参数变化时,Bloc会重新创建并触发PostFetched事件,获取排序后的最新数据。

内容的提问来源于stack exchange,提问作者Rafael Reis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 03:15:48