Navigator路由更改时,InheritedWidget变为空

时间:2019-10-14 03:38:03

标签: flutter

我正在使用InheritedWidget来跟踪身份验证状态。它可以在主应用程序上正常工作。但是,一旦我通过路由更改转到另一个页面,新路由中的组件就找不到继承的窗口小部件。

理想情况下,我希望所有页面/路由都共享相同的身份验证状态。

下面的最小示例给出了NULL指针错误。

import 'package:flutter/material.dart';

void main() => runApp(EntryPoint());

class TestContext extends InheritedWidget {
  const TestContext({
    Key key,
    Widget child,
  }) : super(key: key, child: child);

  final String hello = "foobar";

  static TestContext of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(TestContext) as TestContext;
  }

  @override
  bool updateShouldNotify(TestContext oldWidget) => true;
}

class EntryPoint extends StatelessWidget {
  /// Needed for dialog
  final navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        navigatorKey: navigatorKey,
        title: 'TODO',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: TestContext(
            child: Scaffold(
                body: Center(
                    child: Builder(
                        builder: (context) => FlatButton(
                            child: Text('Launch'),
                            onPressed: () {
                              Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (context) => LoginForm()),
                              );
                            }))))));
  }
}

class LoginForm extends StatefulWidget {
  @override
  LoginFormState createState() {
    return LoginFormState();
  }
}


class LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    // Build a Form widget using the _formKey created above.
    return Scaffold(
        body: Form(
      key: _formKey,
      child: Padding(
          padding: EdgeInsets.all(10.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextFormField(
                decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: 'Enter your JIRA website URL'),
                keyboardType: TextInputType.url,
                textInputAction: TextInputAction.next,
                style: TextStyle(
                  fontSize: 28,
                initialValue: TestContext.of(context).hello,
              ),             
            ],
          )),
    ));
  }
}

════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building LoginForm(dirty, state: LoginFormState#be3a1):
The getter 'hello' was called on null.
Receiver: null
Tried calling: hello

User-created ancestor of the error-causing widget was
    MaterialApp 
package:flutter_greeting_screen/main.dart:63
When the exception was thrown, this was the stack
#0      Object.noSuchMethod  (dart:core-patch/object_patch.dart:51:5)
#1      LoginFormState.build 
package:flutter_greeting_screen/main.dart:144

1 个答案:

答案 0 :(得分:0)

当您使用Navigator.of(context).push(...)Navigator.push(context, ...)Navigator.of(context).pushNamed(...)Navigator.pushNamed(context, ...)时,推入的小部件不是调用Navigator.push的小部件的子级(及其变体),则此小部件是Navigator的最接近实例的子级,该实例包含给定的context,在您的情况下,NavigatorMaterialApp创建,因此如果要为所有路由提供TestContextInheritedWidget必须是Navigator的父级,在您的情况下必须是MaterialApp的父级。

class EntryPoint extends StatelessWidget {
  /// Needed for dialog
  final navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return TestContext(
      child: MaterialApp(
          navigatorKey: navigatorKey,
          title: 'TODO',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
              body: Center(
                  child: Builder(
                      builder: (context) => FlatButton(
                          child: Text('Launch'),
                          onPressed: () {
                            Navigator.push(
                              context,
                              MaterialPageRoute(
                                  builder: (context) => LoginForm()),
                            );
                          }))))),
    );
  }
}

我还建议您查看provider程序包的Provider小部件,它是带有InheritedWidget语法糖的小部件。

  

InheritedWidget的通用实现。它允许公开任何类型的对象,而无需自己手动编写InheritedWidget。

Provider<String>.value(
  value: 'Hello World',
  child: MaterialApp(
    home: Home(),
  )
)
Provider<Auth>(
  builder: (context) => Auth(),
  dispose: (context, auth) => auth.dispose(),
  child: MaterialApp(
    home: Home(),
  )
)