当父母更新自己的状态时,模态底部工作表不会重新呈现自己

问题描述 投票:0回答:2

场景及要求:

我想从存储库加载一些字符串。在此示例中,我们使用 Future.delayed().

从本地存储库加载它们

我们有一个按钮来显示所有的名字。这些名称应该列在 showModalBottomSheet() 中。如果尚未加载名称,则 showModalBottomSheet() 应显示一个循环进度指示器。如果加载了名称,showModalBottomSheet() 应该显示名称。

问题

如果时间少于 3 秒,showModalBottomSheet() 代码会显示循环进度指示器,如果时间超过 3 秒,它还会显示名称。 但是,如果我们在 3 秒过去之前打开 showModalBottomSheet(),并保持打开状态,showModalBottomSheet() 的内容应该自动重新渲染,因为名称在 3 秒后加载,但是 循环进度指示器永远不会消失,因为底部工作表永远不会触发重新渲染,即使列表已经从存储库中加载。

代码

我有以下最小的可复制示例。它编译并正确运行:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(title: 'Title', home: Home());
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: HomeBody(),
    );
  }
}

class HomeBody extends StatefulWidget {
  const HomeBody({super.key});

  @override
  State<StatefulWidget> createState() => _HomeBodyState();
}

class _HomeBodyState extends State<HomeBody> {
  final NamesRepository _namesRepository = NamesRepository();

  late List<String> _names;

  @override
  void initState() {
    super.initState();
    _names = [];
    _namesRepository.loadNames().then((names) => setState(() {
          _names = names;
        }));
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: const Text('Show all names'),
      onPressed: () => _onButtonClick(context),
    );
  }

  void _onButtonClick(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (context) {
        if (_names.isEmpty) {
          return const Center(child: CircularProgressIndicator());
        } else {
          return Column(
            children: _names.map((name) => Text(name)).toList(),
          );
        }
      },
    );
  }
}

class NamesRepository {
  Future<List<String>> loadNames() {
    return Future.delayed(
      const Duration(seconds: 3),
      () => ['John', 'Maria', 'Lewis', 'Danny'],
    );
  }
}


我应该怎么做,以便在从存储库加载列表时模态底部工作表自动重新呈现?如您所见,我在父 Widget 中调用 setState(),但模态底部工作表仍然不会在状态更改时重新呈现自身。我该如何解决这个问题?

flutter flutter-animation
2个回答
0
投票
usually doing a setState() in Parent widget will not have effect in modelsheet because both use different context.
you try instead,
  void _onButtonClick(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (context) {
        return ModelSheet();
      },
    );
  }

and have ModelSheet as StatefulWidget and do the init method here, it will work.

class _ModelSheetState extends State<ModelSheet> {
  final NamesRepository _namesRepository = NamesRepository();

  late List<String> _names;

  @override
  void initState() {
    super.initState();
    _names = [];
    _namesRepository.loadNames().then((names) => setState(() {
          _names = names;
        }));
  }

  @override
  Widget build(BuildContext context) {
    return if (_names.isEmpty) {
          return const Center(child: CircularProgressIndicator());
        } else {
          return Column(
            children: _names.map((name) => Text(name)).toList(),
          );
        }
  }
}

0
投票

我使用了

提供者
ChangeNotifierProvider并在
bottomsheet
中使用了ChangeNotifierProvider.value

这是

HomeBody
的样子:

注意:
确保使用 provider 的构建器 context:
很重要

builder: (context, _) {}

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class HomeBody extends StatelessWidget {
  const HomeBody({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => HomeBodyNotifier(),
      builder: (context, _) {
        return ElevatedButton(
          child: const Text('Show all names'),
          onPressed: () => _onButtonClick(context),
        );
      },
    );
  }

  void _onButtonClick(BuildContext context) {
    showModalBottomSheet(
      context: context,
      builder: (_) {
        return ChangeNotifierProvider<HomeBodyNotifier>.value(
          value: context.watch<HomeBodyNotifier>(),
          builder: (context, _) {
            final names = context.watch<HomeBodyNotifier>().names;

            if (names.isEmpty) {
              return const Center(child: CircularProgressIndicator());
            } else {
              return Column(
                children: names.map((name) => Text(name)).toList(),
              );
            }
          },
        );
      },
    );
  }
}

class HomeBodyNotifier extends ChangeNotifier {
  List<String> names = [];

  HomeBodyNotifier() {
    loadNames();
  }

  void loadNames() {
    Future.delayed(
      const Duration(seconds: 3),
      () {
        names.addAll(['John', 'Maria', 'Lewis', 'Danny']);
        notifyListeners();
      },
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.