Flutter -如何在等待来自小部件API的主题数据后安全地更改主题?

6mzjoqzu  于 2022-12-19  发布在  Flutter
关注(0)|答案(1)|浏览(106)

使用一个正常的设置处理主题与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();
    }
}
xzlaal3s

xzlaal3s1#

我不确定我是否正确理解了你的意思,但是如果你想知道如何通过从API中获取if来更新主题,这里有一个模拟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,
        ),
      ),
    );
  }
}

相关问题