6

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

4 Answers 4

10

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); } } 
Sign up to request clarification or add additional context in comments.

Comments

8

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.

Comments

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.

1 Comment

As a note, the flutter documentation suggests creating the future outside the build method. See api.flutter.dev/flutter/widgets/FutureBuilder-class.html. Copy pasting from the docs: "The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted."
0

You can give the BuildContext as a parameter through to loadCountryData(BuildContext context).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.