发送网络数据
Sending data to the internet is necessary for most apps.
The http package has got that covered, too.
This recipe uses the following steps:
- Add the httppackage.
- Send data to a server using the httppackage.
- Convert the response into a custom Dart object.
- Get a titlefrom user input.
- 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. Sending data to server
#This recipe covers how to create an Album
by sending an album title to the
JSONPlaceholder using the
http.post() method.
Import dart:convert for access to jsonEncode to encode the data:
import 'dart:convert';Use the http.post() method to send the encoded data:
Future<http.Response> createAlbum(String title) {
  return http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/albums'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode(<String, String>{'title': title}),
  );
}The http.post() method returns a Future that contains a Response.
- Futureis a core Dart class for working with asynchronous operations. A Future object 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 createAlbum()method takes an argumenttitlethat is sent to the server to create anAlbum.
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
#Use the following steps to update the createAlbum()
function to return a Future<Album>:
- Convert the response body into a JSON Mapwith thedart:convertpackage.
- If the server returns a CREATEDresponse with a status code of 201, then convert the JSONMapinto anAlbumusing thefromJson()factory method.
- If the server doesn't return a CREATEDresponse with a status code of 201, 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> createAlbum(String title) async {
  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/albums'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode(<String, String>{'title': title}),
  );
  if (response.statusCode == 201) {
    // If the server did return a 201 CREATED response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  } else {
    // If the server did not return a 201 CREATED response,
    // then throw an exception.
    throw Exception('Failed to create album.');
  }
}Hooray! Now you've got a function that sends the title to a server to create an album.
4. Get a title from user input
#Next, create a TextField to enter a title and
a ElevatedButton to send data to 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 createAlbum() method.
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    TextField(
      controller: _controller,
      decoration: const InputDecoration(hintText: 'Enter Title'),
    ),
    ElevatedButton(
      onPressed: () {
        setState(() {
          _futureAlbum = createAlbum(_controller.text);
        });
      },
      child: const Text('Create Data'),
    ),
  ],
)On pressing the Create Data button, make the network request,
which sends the data in the TextField to the server
as a POST request.
The Future, _futureAlbum, 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 asynchronous data sources.
You must provide two parameters:
- The Futureyou want to work with. In this case, the future returned from thecreateAlbum()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 createAlbum() function should throw an exception
even in the case of a "404 Not Found" server response.
If createAlbum() returns null, then
CircularProgressIndicator displays 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> createAlbum(String title) async {
  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/albums'),
    headers: <String, String>{
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode(<String, String>{'title': title}),
  );
  if (response.statusCode == 201) {
    // If the server did return a 201 CREATED response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  } else {
    // If the server did not return a 201 CREATED response,
    // then throw an exception.
    throw Exception('Failed to create 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();
  Future<Album>? _futureAlbum;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Create Data Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text('Create Data Example')),
        body: Container(
          alignment: Alignment.center,
          padding: const EdgeInsets.all(8),
          child: (_futureAlbum == null) ? buildColumn() : buildFutureBuilder(),
        ),
      ),
    );
  }
  Column buildColumn() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        TextField(
          controller: _controller,
          decoration: const InputDecoration(hintText: 'Enter Title'),
        ),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _futureAlbum = createAlbum(_controller.text);
            });
          },
          child: const Text('Create Data'),
        ),
      ],
    );
  }
  FutureBuilder<Album> buildFutureBuilder() {
    return 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();
      },
    );
  }
}除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2025-02-13。 查看文档源码 或者 为本页面内容提出建议.