场景及要求:
我想从存储库加载一些字符串。在此示例中,我们使用 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(),但模态底部工作表仍然不会在状态更改时重新呈现自身。我该如何解决这个问题?
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(),
);
}
}
}
我使用了
提供者的
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();
},
);
}
}