更新网络数据
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 httpImport the http package.
import 'package:http/http.dart' as http;如果你要部署 Android,请编辑 AndroidManifest.xml 文件,添加 Internet 权限。
<!-- Required to fetch data from the internet. -->
<uses-permission android:name="android.permission.INTERNET" />同样,如果你要部署 macOS,请编辑 macos/Runner/DebugProfile.entitlements 和
macos/Runner/Release.entitlements 文件,添加 network client 权限。
<!-- 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. A- Futureobject 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();
            },
          ),
        ),
      ),
    );
  }
}除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2025-02-13。 查看文档源码 或者 为本页面内容提出建议.