如何防止传递BuildContext?

4

目前我从HomeScreenbuild方法中获取BuildContext,然后不得不将其传递到_gridSliver再传递到_storeCard

我应该如何编写代码,以便不需要向下传递上下文?

也许我可以创建一个名为_StoreCard的新私有StatelessWidget,它将拥有自己的build方法和自己的BuildContext

class HomeScreen extends StatelessWidget {
  HomeScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, List<MyStore.Store>>(
        converter: (Store<AppState> store) => store.state.home.stores,
        builder: (BuildContext context, List<MyStore.Store> stores) =>
            CustomScrollView(slivers: <Widget>[_gridSliver(stores, context)]));
  }

  Widget _gridSliver(stores, context) {
    return SliverGrid(            
        delegate: SliverChildListDelegate(List<Widget>.from(stores.map(_storeCard, context))));
  }

  Widget _storeCard(MyStore.Store store, BuildContext context) {
    return InkWell(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(builder: (_) => StoreScreen(storeId: store.id)),
        );
      },
      child: Container(child: Text(store.name))
    );
  }
}

这个问题的另一个实例就是我在子函数中进行导航。
@override
Widget build(BuildContext context) {
  return Column(
    children: [
      WhiteButton(text: "Login with Facebook", onPressed: _loginWithFacebook),
      WhiteButton(text: "Login with Google", onPressed: _loginWithGoogle),
    ])
  )
}

_loginWithFacebook(context) async {
    ...
    var user = User.fromFacebook(result.accessToken.token, json.decode(graphResponse.body));
    await _login(user, context);
  }
}

_loginWithGoogle(context) async {
    ...
  GoogleSignInAccount googleUser = await _googleSignIn.signIn();
  await _login(User.fromGoogle(googleUser), context);
}

_login(user, context) async {
  var fetchedUser = await MeService.getUser(user);
  if (fetchedUser != null) {
    loginSuccess(fetchedUser);
    Navigator.popUntil(context, ModalRoute.withName(MainRoutes.root));
  } else {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (_) => RegisterScreen(user: user)),
    );
  }
}

1
使用无状态小部件最好只是将其传递或将方法迁移到其自己的小部件中。 - Günter Zöchbauer
2个回答

5
要获取一个新的BuildContext,你有两个主要解决方案:
  • 将子树的一部分提取到一个新的小部件中,通常是StatelessWidget。然后从build方法中使用它的BuildContext

  • 使用Builder小部件,它基本上是一个可重用的小部件,用于获取BuildContext

示例:

 @override
 Widget build(BuildContext context) {
  return Builder(
    builder: (context) {
      // do something with this new context
    },
  );
}

如果我需要在一个不构建子树的函数中获取BuildContext实例怎么办?例如,我想获得一个ChangeNotifier以执行某些操作。将上下文传递来传递去有点丑陋... - Wecherowski

2
你需要使用一个使用Inherited Widget的Bloc模式,但还是需要以一种更直接的方式传递context。我建议使用Stephen Grider的这个应用程序来了解整个过程的工作原理。他在教程中解释了如何将整个内容组合在一起,但我不能给你链接,因为那将被视为广告。
首先,你需要创建一个名为Bloc.dart的文件来包含你的逻辑,然后在Provider.dart中创建所谓的提供者。
class Provider extends InheritedWidget {
final bloc = Bloc();

Provider({Key key, Widget child}) : super(key: key, child: child);

bool updateShouldNotify(_) => true;

static Bloc of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
  }
}

在包含Material App的文件中,您需要使用provider将Material App包装起来:
Widget build(BuildContext context) {
return Provider(
  child: MaterialApp(

然后您可以在小部件的三层中的每个其他类中使用提供程序。

class HomeScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
    final bloc = Provider.of(context); // this is where you insert the provider
    return StoreConnector<AppState, List<MyStore.Store>>(
    converter: (Store<AppState> store) => store.state.home.stores,
    builder: (BuildContext context, List<MyStore.Store> stores) =>
        CustomScrollView(slivers: <Widget>[_gridSliver(stores, context)]));
}

Widget _gridSliver(stores) {
   final bloc = Provider.of(context);
   return SliverGrid(            
    delegate: SliverChildListDelegate(List<Widget>.from(stores.map(_storeCard, context))));
 }

Widget _storeCard(MyStore.Store store) {
  final bloc = Provider.of(context);
  return InkWell(
  onTap: () {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (_) => StoreScreen(storeId: store.id)),
    );
  },
  child: Container(child: Text(store.name))
);


 }
}

我对Flutter完全不了解,所以需要谨慎处理,但这就是我会使用的内容。希望能有所帮助。


我喜欢这个解决方案。这是使用BLoC架构(如MVC)的相同事物吗?否则,我没有在其他地方看到它被使用。我确实希望能够做类似的事情,但这个解决方案只是一个框架不建议的变通方法。我宁愿写丑陋的代码,也不愿意使用一些可能会被遗忘的神奇变通方法。 - psyanite

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接