问题
I'm trying to load a json file in a class extending SearchDelegate to search through its content.
I have a method to load this file:
Future<void> loadCountryData() async {
try {
String data = await DefaultAssetBundle
.of(context)
.loadString("assets/data/countries.json");
_countries = json.decode(data);
} catch (e) {
print(e);
}
}
Unfortunately this requires a Buildcontext (context) that seems only to be available in the SearchDelegate build methods (like buildActions, buildLeadings, etc), but no outside like for example in the constructor.
https://docs.flutter.io/flutter/material/SearchDelegate-class.html
As the @override xy build methods in SearchDelegate are called with every change in the search field, I would load my file over and over again, which is of course not ideal. I want to load my file once at the beginning only.
Is there a way to get some sort of get default context that I could use for example in the constructor of SearchDelegate. Like in android (if I remmeber correctly)?
Or can I load an assets file without .of(context)?
回答1:
As DefaultAssetBundle is based on an InheritedWidget you will always need to pass a context.
of just looks up the widget tree based on a BuildContext until it finds a DefaultAssetBundle widget. This means that you cannot retrieve a DefaultAssetBundle object without a BuildContext.
You will need to pass a BuildContext to your method. I could imagine a situation like the following:
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: loadCountryData(context: context),
builder: (BuildContext context, AsyncSnapshot<JSON> jsonData) {
if (!jsonData.hasData) {
return Text('not loaded');
}
return Text('loaded'); // here you want to process your data
},
);
}
/// I am not sure what your decode returns, so I just called it JSON
/// I thought it would make more sense to return the JSON to use it in build
Future<JSON> loadCountryData({BuildContext context}) async {
try {
String data = await DefaultAssetBundle
.of(context)
.loadString("assets/data/countries.json");
return json.decode(data);
} catch(e) {
print(e);
return JSON.empty(); // imagine this exists
}
}
As you can see I passed the BuildContext from the build method. The FutureBuilder also allows to process the data in the build tree directly.
回答2:
Description of function
/// The bundle from the closest instance of this class that encloses
/// the given context.
///
/// If there is no [DefaultAssetBundle] ancestor widget in the tree
/// at the given context, then this will return the [rootBundle].
///
/// Typical usage is as follows:
///
/// ```dart
/// AssetBundle bundle = DefaultAssetBundle.of(context);
/// ```
static AssetBundle of(BuildContext context) {
final DefaultAssetBundle result = context.dependOnInheritedWidgetOfExactType<DefaultAssetBundle>();
return result?.bundle ?? rootBundle;
}
So you may simply use rootBundle instead of DefaultAssetBundle.of(context) for working with assets without context.
回答3:
There is an option to get builtin AssetBundle without specifying a reference to BuildContext. Here is an example of how it could be done:
import 'package:flutter/services.dart'; // is required
Future<void> loadCountryData() async {
try {
// we can access builtin asset bundle with rootBundle
final data = await rootBundle.loadString("assets/data/countries.json");
_countries = json.decode(data);
} catch (e) {
print(e);
}
}
回答4:
You can give the BuildContext as a parameter through to loadCountryData(BuildContext context).
来源:https://stackoverflow.com/questions/51109621/flutter-get-default-context-or-load-assets-without-context