创建StatefulWidget时访问Flutter上下文

时间:2019-06-24 04:27:15

标签: flutter dart

初始化有状态窗口小部件时,我无法访问服务对象。问题来自context中的initState对象。

我正在使用InheritedWidget在我的main.dart文件中注入服务对象

void main() async {
  final sqflite.Database database = await _openDatabase('db.sqlite3');
  runApp(
    Services(
      database: database,
      child: MyApp(),
    ),
  );
}

Services对象非常简单。它将不仅有database作为成员。这个想法是,小部件不需要知道是否正在访问本地数据库,本地缓存或远程服务器。

class Services extends InheritedWidget {
  final Database database;

  const Services({
    Key key,
    @required Widget child,
    @required this.database,
  })  : assert(child != null),
        assert(database != null),
        super(key: key, child: child);

  Future<List<models.Animal>> readAnimals() async {
    return db.readAnimals(database: this.database);
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }

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

当我想从数据库中加载所有动物时,麻烦就出在我的_HomePageState状态。我需要访问Services对象。我无法访问Services中的initState对象,所以我正在使用didChangeDependencies。从堆栈中删除主页时出现问题。似乎didChangeDependences被调用,访问上下文对象是非法的。因此,我创建了一个_initialized标志,可以在didChangeDependencies中使用该标志以确保仅在第一次加载动物时使用。 这看起来非常优雅。有更好的方法吗?

class _HomePageState extends State<HomePage> {
  bool _initialized = false;
  bool _loading = false;
  List<Animal> _animals;

  @override
  Widget build(final BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(Strings.of(this.context).appName),
      ),
      body: _HomeBody(
        loading: this._loading,
        animals: this._animals,
      ),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (!this._initialized) {
      this._initialized = true;
      this._loadAnimals();
    }
  }

  void _loadAnimals() async {
    this.setState(() {
      this._loading = true;
      this._animals = null;
    });

    final List<Animal> animals = await Services.of(this.context).readAnimals();

    this.setState(() {
      this._loading = false;
      this._animals = animals;
    });
  }
}

1 个答案:

答案 0 :(得分:2)

在这种情况下,可以在构建窗口小部件后使用addPostFrameCallback实例的WidgetsBinding来执行一些代码。

  _onLayoutDone(_) {
     this._loadAnimals();
  }

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback(_onLayoutDone);
    super.initState();
  }