网络请求

使用基础的 http

需要配置权限

  • 安卓:在 AndroidManifest.xml 文件里面添加权限 <uses-permission android:name="android.permission.INTERNET" />

安装

flutter pub add http

使用

// get
Future<http.Response> get(String path) async {
    return await http.get(Uri.parse(path));
}

// post
Future<http.Response> post(String path, {Map<String, String>? headers, body}) async {
    return await http.post(Uri.parse(path), headers: headers, body: body);
}

数据转换

flutter 中需要手动编写数据转换类,将 http.Response 转换成 dart 对象。示例如下

import 'dart:convert';

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({
    required this.userId,
    required this.id,
    required this.title,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
    );
  }
}

Future<Album> getAlbum() async {
    final response await http.get(Uri.parse('url'));
    if(response.statusCode == 200) {
        return Album.fromJson(jsonDecode(response.body));
    }else{
        throw Exception('failed to load album');
    }
}

获取数据

在组件的 initState()didChangeDependencies() 中调用

注意,仅 StatefulWidget 组件有效,若想在 StatelessWidget 使用数据,相当于从父组件调用接口获取数据,然后再传入 StatelessWidget 子组件

class _MyAppState extends State<MyApp> {
    late Future<Album> futureAlbum;

    
    void initState() {
        super.initState();
        futureAlbum = getAlbum();
    }
}

显示数据

官网的示例中,初始化 futureAlbum 是使用了 late 且并未赋值,是在 initState() 中获取数据的,若不在 initState() 中获取数据(如通过点击事件获取),则需要为 futureAlbum 赋值(通常赋空值),并且在使用时判断 futureAlbum 是否为空

使用 FutureBuilder 组件

这是一个 fltter 内置的专门用于处理异步数据的组件

class _MyAppState extends State<MyApp> {
    Future<Album>? futureAlbum;

    void _handleTap() {
        setState((){
            futureAlbum = getAlbum();
        });
    }

    
    Widget build(BuildContext context) {
        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();
            }
        )
    }
}

自定义使用数据

将数据值当作普通变量使用,自行处理数据

class _MyAppState extends State<MyApp> {
    Album? album;

    void _handleTap() {
        getAlbum(_counter).then((value) {
            setState(() {
                album = value;
                _counter++;
            });
        });
    }

    
    Widget build(BuildContext context) {
        return Text(album == null ? 'loading...' : album!.title)
    }
}

简单封装如下

需要注意请求头

  • Content-Typeapplication/json,则 post 发送的数据需要进行 jsonEncode 编码
  • Content-Typeapplication/x-www-form-urlencoded,则 post 发送的数据无需进行编码,直接发送 Map<String, String>{} 即可
// config.dart
const baseUrl = 'https://jsonplaceholder.typicode.com';
const baseHeaders = <String, String>{
  'Content-Type': 'application/json; charset=UTF-8',
};

// http.dart
import 'package:flutter_framework/config.dart' as cfg;

var baseUrl = cfg.baseUrl;
var baseHeaders = cfg.baseHeaders;
String queryParams([Map<String, dynamic>? data]) {
  const prefix = '?';
  final result = <String>[];
  data?.forEach((key, value) {
    result.add('$key=$value');
  });
  return result.isNotEmpty ? prefix + result.join('&') : '';
}

Future requestInterceptors(
    String url, String method, Map<String, dynamic>? params) async {
  final token = await getToken();
  final newHeaders = {'Authorization': 'Bearer $token'};
  final Map<String, String> headers = {}
    ..addAll(baseHeaders)
    ..addAll(newHeaders);
  var targetUrl = '$baseUrl$url';
  if (method == 'get') {
    targetUrl = targetUrl + queryParams(params);
  }
  return {'url': Uri.parse(targetUrl), 'headers': headers, 'body': params};
}

class DiyHttp {
  static Future<http.Response> get(String path,
      [Map<String, dynamic>? params]) async {
    final map = await requestInterceptors(path, 'get', params);
    return await http.get(map['url'],
        headers: map['headers']);
  }

  static Future<http.Response> post(String path,
      [Map<String, dynamic>? params, Map<String, String>? headers]) async {
    final map = await requestInterceptors(path, 'post', params);
    return await http.post(map['url'],
        headers: map['headers'], body: map['body']);
  }
}
Last Updated:
Contributors: af