Flutter 3.41 is live! Check out theFlutter 3.41 blog post!
Parse JSON in the background
How to perform a task in the background.
By default, Dart apps do all of their work on a single thread. In many cases, this model simplifies coding and is fast enough that it does not result in poor app performance or stuttering animations, often called "jank."
However, you might need to perform an expensive computation, such as parsing a very large JSON document. If this work takes more than 16 milliseconds, your users experience jank.
To avoid jank, you need to perform expensive computations like this in the background, using a separateIsolate. This recipe uses the following steps:
- Add the
httppackage. - Make a network request using the
httppackage. - Convert the response into a list of photos.
- Move this work to a separate isolate.
1. Add thehttp package
# First, add thehttp package to your project. Thehttp package makes it easier to perform network requests, such as fetching data from a JSON endpoint.
To add thehttp package as a dependency, runflutter pub add:
flutter pub add http2. Make a network request
# This example covers how to fetch a large JSON document that contains a list of 5000 photo objects from theJSONPlaceholder REST API, using thehttp.get() method.
Future<http.Response>fetchPhotos(http.Clientclient)async{returnclient.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));} You're providing anhttp.Client to the function in this example. This makes the function easier to test and use in different environments.
3. Parse and convert the JSON into a list of photos
# Next, following the guidance from theFetch data from the internet recipe, convert thehttp.Response into a list of Dart objects. This makes the data easier to work with.
Create aPhoto class
# First, create aPhoto class that contains data about a photo. Include afromJson() factory method to make it easy to create aPhoto starting with a JSON object.
classPhoto{finalintalbumId;finalintid;finalStringtitle;finalStringurl;finalStringthumbnailUrl;constPhoto({requiredthis.albumId,requiredthis.id,requiredthis.title,requiredthis.url,requiredthis.thumbnailUrl,});factoryPhoto.fromJson(Map<String,dynamic>json){returnPhoto(albumId:json['albumId']asint,id:json['id']asint,title:json['title']asString,url:json['url']asString,thumbnailUrl:json['thumbnailUrl']asString,);}}Convert the response into a list of photos
# Now, use the following instructions to update thefetchPhotos() function so that it returns aFuture<List<Photo>>:
- Create a
parsePhotos()function that converts the response body into aList<Photo>. - Use the
parsePhotos()function in thefetchPhotos()function.
// A function that converts a response body into a List<Photo>.List<Photo>parsePhotos(StringresponseBody){finalparsed=(jsonDecode(responseBody)asList<Object?>).cast<Map<String,Object?>>();returnparsed.map<Photo>(Photo.fromJson).toList();}Future<List<Photo>>fetchPhotos(http.Clientclient)async{finalresponse=awaitclient.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'),);// Synchronously run parsePhotos in the main isolate.returnparsePhotos(response.body);}4. Move this work to a separate isolate
# If you run thefetchPhotos() function on a slower device, you might notice the app freezes for a brief moment as it parses and converts the JSON. This is jank, and you want to get rid of it.
You can remove the jank by moving the parsing and conversion to a background isolate using thecompute() function provided by Flutter. Thecompute() function runs expensive functions in a background isolate and returns the result. In this case, run theparsePhotos() function in the background.
Future<List<Photo>>fetchPhotos(http.Clientclient)async{finalresponse=awaitclient.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'),);// Use the compute function to run parsePhotos in a separate isolate.returncompute(parsePhotos,response.body);}Notes on working with isolates
# Isolates communicate by passing messages back and forth. These messages can be primitive values, such asnull,num,bool,double, orString, or simple objects such as theList<Photo> in this example.
You might experience errors if you try to pass more complex objects, such as aFuture orhttp.Response between isolates.
As an alternate solution, check out theworker_manager orworkmanager packages for background processing.
Complete example
#import'dart:async';import'dart:convert';import'package:flutter/foundation.dart';import'package:flutter/material.dart';import'package:http/http.dart'ashttp;Future<List<Photo>>fetchPhotos(http.Clientclient)async{finalresponse=awaitclient.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'),);// Use the compute function to run parsePhotos in a separate isolate.returncompute(parsePhotos,response.body);}// A function that converts a response body into a List<Photo>.List<Photo>parsePhotos(StringresponseBody){finalparsed=(jsonDecode(responseBody)asList<Object?>).cast<Map<String,Object?>>();returnparsed.map<Photo>(Photo.fromJson).toList();}classPhoto{finalintalbumId;finalintid;finalStringtitle;finalStringurl;finalStringthumbnailUrl;constPhoto({requiredthis.albumId,requiredthis.id,requiredthis.title,requiredthis.url,requiredthis.thumbnailUrl,});factoryPhoto.fromJson(Map<String,dynamic>json){returnPhoto(albumId:json['albumId']asint,id:json['id']asint,title:json['title']asString,url:json['url']asString,thumbnailUrl:json['thumbnailUrl']asString,);}}voidmain()=>runApp(constMyApp());classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContextcontext){constappTitle='Isolate Demo';returnconstMaterialApp(title:appTitle,home:MyHomePage(title:appTitle),);}}classMyHomePageextendsStatefulWidget{constMyHomePage({super.key,requiredthis.title});finalStringtitle;@overrideState<MyHomePage>createState()=>_MyHomePageState();}class_MyHomePageStateextendsState<MyHomePage>{lateFuture<List<Photo>>futurePhotos;@overridevoidinitState(){super.initState();futurePhotos=fetchPhotos(http.Client());}@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:Text(widget.title)),body:FutureBuilder<List<Photo>>(future:futurePhotos,builder:(context,snapshot){if(snapshot.hasError){returnconstCenter(child:Text('An error has occurred!'));}elseif(snapshot.hasData){returnPhotosList(photos:snapshot.data!);}else{returnconstCenter(child:CircularProgressIndicator());}},),);}}classPhotosListextendsStatelessWidget{constPhotosList({super.key,requiredthis.photos});finalList<Photo>photos;@overrideWidgetbuild(BuildContextcontext){returnGridView.builder(gridDelegate:constSliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2,),itemCount:photos.length,itemBuilder:(context,index){returnImage.network(photos[index].thumbnailUrl);},);}}
Unless stated otherwise, the documentation on this site reflects Flutter 3.38.6. Page last updated on 2025-08-19.View source orreport an issue.