扑动-如何安全地改变主题后等待主题数据从API在一个小部件?



使用正常设置来处理带有ChangeNotifier的主题,它会通知整个应用程序/它下面的所有内容-应该重新绘制某些内容

这种方法似乎很通用,并且有多个"指南"。这样做。当点击一个按钮来改变它的时候,这个效果很好。但是,如果主题的数据来自API -在呈现小部件之前,我们可以在哪里安全地更新相同的值?

这是一个示例代码,其中ThemeData以某种方式"下载"。并且应该在渲染视图之前更新,一旦StreamBuilder完成。当然,这会导致正在下载的Widget在构建时被重新绘制,所以我得到了一个警告。

如何解决这个问题?主题可以只是下载和动态更改的单一颜色。到目前为止,我还没有看到主题在一个小部件内被改变,而"主部件"被改变。是不变的。不知道什么是最好的方法来解决这个(或类似的)问题-因为这在一个主要基于网络的世界里不可能不常见。

编辑# 1:只是为了澄清-主题可能会根据正在加载的小部件/页面/屏幕而改变,它不是"一次性的事情"。在开始时初始化它,但每个屏幕都被加载-根据在线API数据自定义特定的页面。

示例代码:

void main() {

runApp(ChangeNotifierProvider(
create: (context) => ThemeConfig(),
child: MyApp()
));
}
class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {

return Consumer<ThemeConfig>(builder: (context, state, child)
{
return MaterialApp(
theme: state.getTheme()
)
});
}
}
class _MyScreen extends State<MyScreen>
{
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Api.downloadTheme(),
builder: (context, snapshot)
{
// If OK render screen - But where to safely set the "Theme" from API?
return MyWidget(context.data)
});
)
}
}
class _MyWidget extends State<MyWidget>
{
@override
void initState() {
super.initState();
// This will cause the Widget tree to be redrawn while it's drawing and not work at all
// So when I've downloaded the data - where can this safely be changed?
Provider.of<ThemeConfig>(context).setTheme(widget.data.theme);
}
@override
Widget build(BuildContext context) {
return Container();
}
}

我不确定我是否正确理解了你,但如果你想知道如何通过从api中获取来更新主题,这里是模拟api调用更新主题的示例:

void main() {
runApp(
ChangeNotifierProvider(
lazy: false, // triggering ThemeConfig constructor
create: (context) => ThemeConfig(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ThemeConfig>(builder: (context, state, child) {
return MaterialApp(
theme: state.theme,
home: MyScreen(),
);
});
}
}
class ThemeConfig with ChangeNotifier {
ThemeConfig() {
// trigger theme fetch
getTheme();
}
ThemeData theme = ThemeData(primarySwatch: Colors.blue); // initial theme
Future<void> getTheme() async {
// TODO: fetch your theme data here and then update it like below
await Future.delayed(Duration(seconds: 3)); // simulating waiting for response
theme = ThemeData(primarySwatch: Colors.red);
notifyListeners();
}
}
class MyScreen extends StatelessWidget {
const MyScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(), // notice how the colors change
body: Center(
child: Container(
height: 200,
width: 200,
color: Theme.of(context).primaryColor,
),
),
);
}
}

最新更新