Флаттер: унаследованные виджеты и маршруты

Я хочу иметь унаследованный виджет в корне моего приложения, который будет содержать моих поставщиков данных, которые я буду использовать в приложении. Итак, у меня есть этот унаследованный виджет, но каждый раз, когда я пытаюсь загрузить его, я получаю это The getter 'data' was called on null и я не могу понять, почему.

Итак, вот мой main.dart:

void main() => runApp(new MatAppRoot());

class MatAppRoot extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'MyCoolApp',
      routes: <String, WidgetBuilder>{
        'Login': (BuildContext context) => new LoginPage(),
        'Cool': (BuildContext context) => new CoolPage(),
      },
      home: new CoolApp(),
    );
  }
}

class CoolAppextends StatefulWidget {

  final Widget child;

  CoolApp({this.child});

  @override
  CoolAppState createState() => new CoolAppState();

  static CoolAppState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(CoolInherit) as CoolInherit).data;
  }
}

class CoolAppState extends State<CoolApp> {
  String randomString = 'AYEEAS!!!';

  @override
  void initState() { super.initState();
    Navigator.of(context).pushNamedAndRemoveUntil('Login', (Route<dynamic> route) => false);
  }

  @override
  Widget build(BuildContext context) {
    return new CoolInherit(
      data: this,
      child: new LoginPage(),
    );
  }

}

class CoolInherit extends InheritedWidget {

  final CoolAppState data;

  CoolInherit({
    Key key,
    this.data,
    Widget child,
  }): super(
    key: key,
    child: child
  );

  @override
  bool updateShouldNotify(CoolInherit old) {
    return true;
  }
}

тогда мой LoginPage в основном перенаправляет после входа в систему, как это:

if (logInSuccessful) {
  Navigator.of(context).pushNamedAndRemoveUntil('Cool', (Route<dynamic> route) => false);
}

В моем Cool страница Я пытаюсь загрузить другую страницу, нажимая на кнопку вроде этой:

viewCoolDetails() {
  Navigator.push(
    context,
    new MaterialPageRoute(builder: (context) => new CoolDetailsPage()),
  );
}

но по моему CoolDetailsPage он падает, когда я делаю это:

@override
Widget build(BuildContext context) {
  final inheritedWidget = CoolApp.of(context);
  print(inheritedWidget.randomString);   <-- ERROR: The getter 'data' was called on null
  return new Text('Cool!');
}

Детали ошибки:

I/flutter ( 6129): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 6129): The following NoSuchMethodError was thrown building CoolDetailsPage(dirty, state:
I/flutter ( 6129): _CoolDetailsPage#ba0bb):
I/flutter ( 6129): The getter 'data' was called on null.
I/flutter ( 6129): Receiver: null
I/flutter ( 6129): Tried calling: data
I/flutter ( 6129):
I/flutter ( 6129): When the exception was thrown, this was the stack:
I/flutter ( 6129): #0      Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:46:5)
I/flutter ( 6129): #1      CoolApp.of (/lib/main.dart:56:83)
... etc etc

main.dart: 56 является return (context.inheritFromWidgetOfExactType(CoolInherit) as CoolInherit).data; и поэтому, если моя детективная работа находится на должном уровне, я подозреваю, что это связано с навигацией / контекстом, что препятствует доступу моего окончательного виджета к унаследованному виджету, но я не уверен в этом.

ОБНОВИТЬ:

лучшее, что я могу сказать, мне нужно вставить мой InheritedWidget на более высоком уровне; перед навигатором. поэтому я вставил это в MaterialApp:

builder: (context, child) {
  return new CoolApp(child: child);
},

но это не видел, чтобы работать...

E/flutter (32321): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter (32321): Navigator operation requested with a context that does not include a Navigator.
E/flutter (32321): The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
E/flutter (32321): #0      Navigator.of.<anonymous closure> (package:flutter/src/widgets/navigator.dart:1180:9)
E/flutter (32321): #1      Navigator.of (package:flutter/src/widgets/navigator.dart:1187:6)

1 ответ

У меня долгое время была такая же проблема, и я понял, что если вы обернете MaterialApp унаследованным виджетом, ваши данные будут доступны через все приложение. Но в вашем случае вам необходимо передать данные после входа пользователя в систему, поэтому для этого вам нужно создать новый навигатор и обернуть его своим унаследованным виджетом. Вы можете увидеть этот проект https://github.com/johnstef99/inherited-widget-demo

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'InheritedWidget Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyNav(),
    );
  }
}

Route generatePage(child) {
  return MaterialPageRoute(builder: (context) => child);
}

class MyNav extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MyData(
      data: 'omg',
      child: Navigator(
        onGenerateRoute: (settings) {
          switch (settings.name) {
            case 'page1':
              return generatePage(PageOne());
            case 'page2':
              return generatePage(PageTwo());
            case 'page3':
              return generatePage(PageThree());
          }
        },
        initialRoute: 'page1',
      ),
    );
  }
}

class MyData extends InheritedWidget {
  MyData({Key key, this.child, this.data}) : super(key: key, child: child);

  final Widget child;
  final String data;

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

  @override
  bool updateShouldNotify(MyData oldWidget) {
    return true;
  }
}

class PageOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 1'),
      ),
      backgroundColor: Colors.red,
      body: RaisedButton(
        child: Text("Goto page 2, data=${MyData.of(context).data}"),
        onPressed: () {
          Navigator.of(context)
              .push(MaterialPageRoute(builder: (_) => PageTwo()));
        },
      ),
    );
  }
}

class PageTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 2'),
      ),
      backgroundColor: Colors.red,
      body: RaisedButton(
        child: Text("Goto page 3, data=${MyData.of(context).data}"),
        onPressed: () {
          Navigator.of(context)
              .push(MaterialPageRoute(builder: (_) => PageThree()));
        },
      ),
    );
  }
}

class PageThree extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 3'),
      ),
      backgroundColor: Colors.green,
      body: RaisedButton(
        child: Text("Goto page 4, data=${MyData.of(context).data}"),
        onPressed: null,
      ),
    );
  }
}

Это потому, что вы пытаетесь получить доступ CoolApp который находится в пути / с другого маршрута (динамический).

Но внутри вашего динамического маршрута нет CoolApp, Так CoolApp.of(context) возвращает ноль, и, следовательно, доступ .data сбои.

Вам нужно найти способ иметь CoolApp экземпляр внутри вашего нового маршрута.

Для получения дополнительной информации взгляните на Получить доступ к контексту InheritedWidget

Другие вопросы по тегам