简体   繁体   English

如何防止传递BuildContext?

[英]How to prevent passing down BuildContext?

Currently I get the BuildContext from the build method in HomeScreen , and then I have to pass it down to _gridSliver then down to _storeCard . 当前,我从HomeScreenbuild方法获取BuildContext ,然后必须将其传递给_gridSliver然后传递给_storeCard

How can I write the code so that I don't need to pass the context down? 如何编写代码,这样我就不需要传递上下文了?

Maybe I can create a new private StatelessWidget called _StoreCard that will have its own build method and thus its own BuildContext ? 也许我可以创建一个名为_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))
    );
  }
}

Another instance of this problem is I navigate on a child function. 此问题的另一个实例是我在子函数上导航。

@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)),
    );
  }
}

To get a new BuildContext , you have two main solutions: 要获得新的BuildContext ,您有两个主要解决方案:

  • Extract part of the subtree into a new widget, typically StatelessWidget . 将子树的一部分提取到新的小部件(通常为StatelessWidget And then use it's BuildContext from the build method 然后从build方法使用它的BuildContext

  • Use Builder widget, which is basically a reusable widget made to obtain a BuildContext : 使用Builder小部件,它基本上是可重用的小部件,用于获取BuildContext

Example: 例:

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

You have to use a Bloc pattern that uses an Inherited Widget, but still you'll have to pass context, but in a more straight forward way. 您必须使用使用Inherited Widget的Bloc模式,但仍然必须以更直接的方式传递上下文。 I recommend using this app by Stephen Grider, to figure out how the whole thing works. 我建议使用Stephen Grider的这个应用程序 ,以了解整个过程。 He explains in his tutorial how to put the whole thing together but I can't link you to that because that would be advertising. 他在教程中解释了如何将整个内容放在一起,但我无法将您链接到该内容,因为那将是广告。

The idea is, you first create a file Bloc.dart that is going to contain your logic, then you create what is called a Provider, in a Provider.dart. 这个想法是,首先创建一个包含逻辑的文件Bloc.dart,然后在Provider.dart中创建一个称为Provider的文件。

Provider.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;
  }
}

In your file that contains the Material App, you wrap the material App with the provider: 在包含Material App的文件中,使用提供者包装Material App:

Widget build(BuildContext context) {
return Provider(
  child: MaterialApp(

And then you use the provider in every other class down the three of widgets. 然后在其他三个小类中的每个其他类中使用提供程序。

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))
);


 }
}

I'm a total noob with flutter and take everything with grain of salt, but this is what I would use. 我是个全神贯注的菜鸟,可以撒些盐,但这就是我会用的。 Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM