Flutter/Dart : synchronous way to read file in asset folder

1,062

In general reading the file asynchronously should not be a problem at all and is actually recommended. You don't want to block the UI while waiting for the file to be loaded and parsed.

So this means you probably have a small mistake in your code that is causing this problem. Since you didn't list any code I cannot really pin point the problem but I do have an example that I have been using which might help you.

In my example App (which you can find here) I use the following JSON file (the actual file is bigger but this gives you an idea):

places.json

{
    "html_attributions": [],
    "results": [
        {
            "geometry": {
                "location": {
                    "lat": 52.221525,
                    "lng": 6.895323999999999
                },
                "viewport": {
                    "northeast": {
                        "lat": 52.2231135302915,
                        "lng": 6.897867100000001
                    },
                    "southwest": {
                        "lat": 52.2204155697085,
                        "lng": 6.894476299999999
                    }
                }
            },
            "icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
            "id": "6e274492c5dfc4100a68d47e70d8f69e1bbef40a",
            "name": "Fellini Enschede",
            "opening_hours": {
                "open_now": true
            },
            "photos": [
                {
                    "height": 3024,
                    "html_attributions": [
                        "<a href=\"https://maps.google.com/maps/contrib/109213220123895357242/photos\">Martijn Hoppenbrouwer</a>"
                    ],
                    "photo_reference": "CmRaAAAAPhSAiB4-VuS-AmUdRxjBUIooQJg68zdQyLRmPJGOB7G_2xh9deq3lcI9ChPnlaTZPmQtUQ_uBqWenNLif0Okqqr5ErAGPBlhoIpcbW6iBt0Uto1qjOjRPuWTRrrL_imlEhDjC25hFY3jKzMU04ZbXUaqGhQh13kxVLaYj-aNxDnsqWfRvfWFBA",
                    "width": 4032
                }
            ],
            "place_id": "ChIJ8XcydnMUuEcRCFdOhmTnLsY",
            "plus_code": {
                "compound_code": "6VCW+J4 Enschede, Netherlands",
                "global_code": "9F486VCW+J4"
            },
            "rating": 4.1,
            "reference": "CmRSAAAAtmshr7ZCjO5X5Zb8ZHNGgek6CzPUBXHBEtP7AR_A8vg10GMpcMCdk_S1Y141Nv5G3f7ed9XFPaSjU6aR_lHoJ6PwNx3lQL7HJf9ai9G2aExbs5KHuwacwTqUV3ocI1RkEhDWe_4cklgqwe94iyEOg4fNGhSZL88S4ZAde2kWm0VAwm8gObCCjg",
            "scope": "GOOGLE",
            "types": [
                "night_club",
                "bar",
                "restaurant",
                "food",
                "point_of_interest",
                "establishment"
            ],
            "vicinity": "2, Bolwerkstraat, Enschede"
        },
        {
            "geometry": {
                "location": {
                    "lat": 52.22302359999999,
                    "lng": 6.893403099999998
                },
                "viewport": {
                    "northeast": {
                        "lat": 52.22446798029149,
                        "lng": 6.894722280291502
                    },
                    "southwest": {
                        "lat": 52.22177001970849,
                        "lng": 6.892024319708497
                    }
                }
            },
            "icon": "https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png",
            "id": "a18ff7853e5f6eb545fcb93fd55c85cf915bd301",
            "name": "'T Bölke",
            "photos": [
                {
                    "height": 1152,
                    "html_attributions": [
                        "<a href=\"https://maps.google.com/maps/contrib/115418315198000399419/photos\">&#39;t Bölke</a>"
                    ],
                    "photo_reference": "CmRaAAAAhRaUOljchj6XFtlC7sB-93l4pfcEMTClAG6pZbPkE4KQ64Y9kPY0cAZ6uozFJdD_aB7BHPfIbhY2EU-47b8v_lWjaZcaQX_YIhh1Hqbz9tMCv-brLfxYzykbpfYMVaa2EhD_9RqnDMDDYjzR_lJvYW_UGhQi4dnTzDTgIiaRHNVrlmfv28qRvw",
                    "width": 2048
                }
            ],
            "place_id": "ChIJX2MAvHQUuEcRY9HmmyKX1oI",
            "plus_code": {
                "compound_code": "6VFV+69 Enschede, Netherlands",
                "global_code": "9F486VFV+69"
            },
            "rating": 4.1,
            "reference": "CmRSAAAAZ-3uQbzsSLjQRshhJ9L6J7iOOMV2T7HL-ELqrufGKXflCbVHaEDD3-eNoAvVsF0k4muHAQT-rQaf-bYPFuy3BafMyLzMnscR1cTuCVwpLvo-Gh9O62PJmhMtSqbcJvoCEhDVHVVCEIxrmEhHY96ucRlHGhTAU1zn9KcPxRsZslAvinU1mBmtSQ",
            "scope": "GOOGLE",
            "types": [
                "night_club",
                "bar",
                "point_of_interest",
                "establishment"
            ],
            "vicinity": "Molenstraat 6-8, Enschede"
        }
    ]
}

Next I have a helper Dart file which defines the Place class and contains a method that will read the file and transforms the JSON items into instances of the Place class, which looks like this:

places.dart

import 'dart:async';
import 'dart:convert';

import 'package:flutter/services.dart';

class Place {
  final String name;
  final double rating;
  final String address;

  Place.fromJson(Map jsonMap) : 
    name = jsonMap['name'],
    rating = jsonMap['rating']?.toDouble() ?? -1.0,
    address = jsonMap['vicinity'];

  String toString() => 'Club: $name';
}

Future<Stream<Place>> getPlaces() async {
  return new Stream.fromFuture(rootBundle.loadString('assets/places.json'))
    .transform(json.decoder)
    .expand((jsonBody) => (jsonBody as Map)['results'])
    .map((jsonPlace) => new Place.fromJson(jsonPlace));
}

And finally I have a very simple UI that will call the getPlaces method and show al records in a ListView:

main.dart

import 'package:flutter/material.dart';

import 'places.dart';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  List<Place> _places = <Place>[];

  @override
  void initState() {
    super.initState();

    listenForPlaces();
  }

  void listenForPlaces() async {
    var stream = await getPlaces();

    stream.listen(
      (place) => 
        setState(
          () => _places.add(place)
        )
      );
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new ListView(
          children: _places.map((place) => new PlaceWidget(place)).toList(),
        ),
      ),
    );
  }
}

class PlaceWidget extends StatelessWidget {
  final Place place;

  PlaceWidget(this.place);
  
  @override
  Widget build(BuildContext context) {
    var ratingColor = Color.lerp(Colors.red, Colors.green, place.rating / 5);

    var listTile = new ListTile(
      leading: new CircleAvatar(
        child: new Text(place.rating.toString()),
        backgroundColor: ratingColor,
      ),
      title: new Text(place.name),
      subtitle: new Text(place.address),
    );

    return new Dismissible(
      key: new Key(place.name),
      background: new Container(color: Colors.green),
      secondaryBackground: new Container(color: Colors.red),
      onDismissed: (dir) {
        if(dir == DismissDirection.startToEnd) {
          print("You liked ${place.name}");
        } else {
          print("You didn't like ${place.name}");
        }
      },
      child: listTile,
    );
  }
}
Share:
1,062
Sudarshan
Author by

Sudarshan

Updated on December 26, 2022

Comments

  • Sudarshan
    Sudarshan over 1 year

    I am trying to read JSON file in assets folder and deserializing it to object. String fromJson = await rootBundle.loadString('assets/File.json'); deserialization function is async so it returns even before actually reading JSON file and deserializing it. that's why there are multiple calls happening to this deserialization function from different places in project.

    All async calls are queued before executing first await call ie. await rootBundle.loadString('assets/File.json'); so the file is being read multiple times.

    Is there a way to wait for file to read or wait till deserialization happens and then return back to the caller of deserialization function? or synchronous way to read file in asset folder ? I want file to read only once and wait for deserialization to happen.