Update data over the internet
Updating data over the internet is necessary for most apps. The http package has got that covered!
This recipe uses the following steps:
- Add the
httppackage. - Update data over the internet using the
httppackage. - Convert the response into a custom Dart object.
- Get the data from the internet.
- Update the existing
titlefrom user input. - Update and display the response on screen.
1. Add the http package
# To add the http package as a dependency, run flutter pub add:
$ flutter pub add http Import the http package.
import 'package:http/http.dart' as http; If you are deploying to Android, edit your AndroidManifest.xml file to add the Internet permission.
<!-- Required to fetch data from the internet. --> <uses-permission android:name="android.permission.INTERNET" /> Likewise, if you are deploying to macOS, edit your macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements files to include the network client entitlement.
<!-- Required to fetch data from the internet. --> <key>com.apple.security.network.client</key> <true/> 2. Updating data over the internet using the http package
# This recipe covers how to update an album title to the JSONPlaceholder using the http.put() method.
Future<http.Response> updateAlbum(String title) { return http.put( Uri.parse('https://jsonplaceholder.typicode.com/albums/1'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode(<String, String>{'title': title}), ); } The http.put() method returns a Future that contains a Response.
-
Futureis a core Dart class for working with async operations. AFutureobject represents a potential value or error that will be available at some time in the future. - The
http.Responseclass contains the data received from a successful http call. - The
updateAlbum()method takes an argument,title, which is sent to the server to update theAlbum.
3. Convert the http.Response to a custom Dart object
# While it's easy to make a network request, working with a raw Future<http.Response> isn't very convenient. To make your life easier, convert the http.Response into a Dart object.
Create an Album class
# First, create an Album class that contains the data from the network request. It includes a factory constructor that creates an Album from JSON.
Converting JSON with pattern matching is only one option. For more information, see the full article on JSON and serialization.
class Album { final int id; final String title; const Album({required this.id, required this.title}); factory Album.fromJson(Map<String, dynamic> json) { return switch (json) { {'id': int id, 'title': String title} => Album(id: id, title: title), _ => throw const FormatException('Failed to load album.'), }; } } Convert the http.Response to an Album
# Now, use the following steps to update the updateAlbum() function to return a Future<Album>:
- Convert the response body into a JSON
Mapwith thedart:convertpackage. - If the server returns an
UPDATEDresponse with a status code of 200, then convert the JSONMapinto anAlbumusing thefromJson()factory method. - If the server doesn't return an
UPDATEDresponse with a status code of 200, then throw an exception. (Even in the case of a "404 Not Found" server response, throw an exception. Do not returnnull. This is important when examining the data insnapshot, as shown below.)
Future<Album> updateAlbum(String title) async { final response = await http.put( Uri.parse('https://jsonplaceholder.typicode.com/albums/1'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode(<String, String>{'title': title}), ); if (response.statusCode == 200) { // If the server did return a 200 OK response, // then parse the JSON. return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>); } else { // If the server did not return a 200 OK response, // then throw an exception. throw Exception('Failed to update album.'); } } Hooray! Now you've got a function that updates the title of an album.
Get the data from the internet
#Get the data from internet before you can update it. For a complete example, see the Fetch data recipe.
Future<Album> fetchAlbum() async { final response = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/albums/1'), ); if (response.statusCode == 200) { // If the server did return a 200 OK response, // then parse the JSON. return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>); } else { // If the server did not return a 200 OK response, // then throw an exception. throw Exception('Failed to load album'); } } Ideally, you will use this method to set _futureAlbum during initState to fetch the data from the internet.
4. Update the existing title from user input
# Create a TextField to enter a title and a ElevatedButton to update the data on server. Also define a TextEditingController to read the user input from a TextField.
When the ElevatedButton is pressed, the _futureAlbum is set to the value returned by updateAlbum() method.
Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Padding( padding: const EdgeInsets.all(8), child: TextField( controller: _controller, decoration: const InputDecoration(hintText: 'Enter Title'), ), ), ElevatedButton( onPressed: () { setState(() { _futureAlbum = updateAlbum(_controller.text); }); }, child: const Text('Update Data'), ), ], ); On pressing the Update Data button, a network request sends the data in the TextField to the server as a PUT request. The _futureAlbum variable is used in the next step.
5. Display the response on screen
# To display the data on screen, use the FutureBuilder widget. The FutureBuilder widget comes with Flutter and makes it easy to work with async data sources. You must provide two parameters:
- The
Futureyou want to work with. In this case, the future returned from theupdateAlbum()function. - A
builderfunction that tells Flutter what to render, depending on the state of theFuture: loading, success, or error.
Note that snapshot.hasData only returns true when the snapshot contains a non-null data value. This is why the updateAlbum function should throw an exception even in the case of a "404 Not Found" server response. If updateAlbum returns null then CircularProgressIndicator will display indefinitely.
FutureBuilder<Album>( future: _futureAlbum, builder: (context, snapshot) { if (snapshot.hasData) { return Text(snapshot.data!.title); } else if (snapshot.hasError) { return Text('${snapshot.error}'); } return const CircularProgressIndicator(); }, ); Complete example
#import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; Future<Album> fetchAlbum() async { final response = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/albums/1'), ); if (response.statusCode == 200) { // If the server did return a 200 OK response, // then parse the JSON. return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>); } else { // If the server did not return a 200 OK response, // then throw an exception. throw Exception('Failed to load album'); } } Future<Album> updateAlbum(String title) async { final response = await http.put( Uri.parse('https://jsonplaceholder.typicode.com/albums/1'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode(<String, String>{'title': title}), ); if (response.statusCode == 200) { // If the server did return a 200 OK response, // then parse the JSON. return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>); } else { // If the server did not return a 200 OK response, // then throw an exception. throw Exception('Failed to update album.'); } } class Album { final int id; final String title; const Album({required this.id, required this.title}); factory Album.fromJson(Map<String, dynamic> json) { return switch (json) { {'id': int id, 'title': String title} => Album(id: id, title: title), _ => throw const FormatException('Failed to load album.'), }; } } void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() { return _MyAppState(); } } class _MyAppState extends State<MyApp> { final TextEditingController _controller = TextEditingController(); late Future<Album> _futureAlbum; @override void initState() { super.initState(); _futureAlbum = fetchAlbum(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Update Data Example', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), ), home: Scaffold( appBar: AppBar(title: const Text('Update Data Example')), body: Container( alignment: Alignment.center, padding: const EdgeInsets.all(8), child: FutureBuilder<Album>( future: _futureAlbum, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasData) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text(snapshot.data!.title), TextField( controller: _controller, decoration: const InputDecoration( hintText: 'Enter Title', ), ), ElevatedButton( onPressed: () { setState(() { _futureAlbum = updateAlbum(_controller.text); }); }, child: const Text('Update Data'), ), ], ); } else if (snapshot.hasError) { return Text('${snapshot.error}'); } } return const CircularProgressIndicator(); }, ), ), ), ); } } Unless stated otherwise, the documentation on this site reflects Flutter 3.35.5. Page last updated on 2025-10-28. View source or report an issue.