如何在 Flutter 中获取有关提供商上下文更改的网络数据

How to fetch network data on provider context change in Flutter

我有一个带有一些奇怪功能的 Flutter 应用程序,但它是必需的。该应用程序具有包含在 IndexedStack 中的页面的 BottomNavigationBar,因此如果您转到此 IndexedStack 的其他页面,页面不会重新加载并保持持久。

class _MyHomePageState extends State<MyHomePage> {
//...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: selectedIndex,
        children: bottomBarPages(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: navItems,
        currentIndex: selectedIndex,
        onTap: _onNavItemTapped,
      ),
    );
  }
}

其中一个页面是个人资料页面,您可以在其中选择用户列表。我使用如下所示的 ChangeNotifier:


class MainUserInfo extends ChangeNotifier {
  List<UserInfo> _users;
  UserInfo _chosenUser;
  //... some get/set/etc methods here
}

其他 BottomNavigationBar 页面之一 TheOtherPage 取决于所选用户 (_chosenUser),在 initState 上它从网络加载数据:

class TheOtherPage extends StatefulWidget {
//...

  bool isLoading = true;

  @override
  void initState() {
    super.initState();
    getAccountData();
  }

  void getAccountData() async {
    //some fetch happening here
    
    setState(() {
      model = response.data;
      isLoading = false;
    });
  }
}

问题是字段_chosenUser 更改后,我需要TheOtherPage 从网络加载所选用户的数据。但我不知道如何处理它。如果我使用 context.read 或 context.select 它肯定会跟踪更改的 _chosenUser,但不会获取新选择的用户的数据:

var mainUserInfo = context.read<MainUserInfo>();
//or
var chosenUser = context.select<MainUserInfo, UserInfo>((userInfo) => userInfo.chosenUser);

而且我不想在 UserInfo 对象中保存 TheOtherPage 所需的数据,因为数据太多,并且在 MainUserInfo._users 中为每个用户加载这么多数据不是一个选项。 此外,我将能够跟踪加载状态,因为使用“bool Isloading”我可以在获取所需数据时显示一些花哨的占位符。

所以一般的问题是如何让 TheOtherPage 在 ProfilePage 上的 ChangeNotifier 的更改字段上获取数据。也许我的整个方法不好,我不需要在这里使用 Provider 和 ChangeNotifier,我希望看到任何建议。谢谢

这是提供商试图解决的典型案例:在 2 个小部件之间共享数据

这是你的树结构:

Scaffold
 \ IndexedStack
    \ ProfilePage  <- share MainUserInfo 
    \ TheOtherPage  <- share MainUserInfo 

问题是您希望 TheOtherPage 收听 MainUserInfo 但它不收听。 (因为statefulwidget中的initState()只会被调用一次,即使状态改变了)

我建议你可以在build方法里面监听provider,用FutureBuilder来处理加载状态:

class _TheOtherPageState extends State<TheOtherPage> {
//...

  @override
  void initState() {
    super.initState();
    // do nothing here
  }

  @override
  Widget build(BuildContext context) {      
    // rebuild everytime the MainUserInfo get notified by watching it
    // final mainUserInfo = context.watch<MainUserInfo>();

    // rebuild everytime the chosenUser change inside MainUserInfo
    final chosenUser = context.select<MainUserInfo, UserInfo>((userInfo) => userInfo.chosenUser);

    return FutureBuilder(
      future: getAccountData(chosenUser).
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if(snapshot.connectionState == ConnectionState.done && snapshot.hasData){
          model = snapshot.data;

          // return a page with the model
        } else {

          // return the loading placeholders 
        }
      }
    );
  }

  Future getAccountData(user) async {
    //some fetch happening here with the user
    // ...

    return response.data;
  }
}

您还可以尝试一件事:使用 const 关键字 ()

  ...
  body: IndexedStack(
    index: selectedIndex,
    children: [
      ProfilePage(),
      const TheOtherPage(),    // here
    ],
  ),
  ...

class TheOtherPage extends StatefulWidget {
  const TheOtherPage({Key key}) : super(key: key);   // and here

  @override
  _TheOtherPageState createState() => _TheOtherPageState();
}