Skip to content
On this page

Flutter网络请求

在现代移动应用开发中,网络请求是必不可少的功能。Flutter提供了多种方式进行网络请求,从基础的HTTP请求到高级的网络状态管理。本章将详细介绍Flutter中的网络编程实践。

网络请求基础

添加网络依赖

首先,在pubspec.yaml中添加网络请求所需的依赖:

yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.5

基础HTTP请求

dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class BasicNetworkingDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('基础网络请求')),
      body: Center(
        child: ElevatedButton(
          onPressed: _makeBasicRequest,
          child: Text('发起GET请求'),
        ),
      ),
    );
  }

  Future<void> _makeBasicRequest() async {
    try {
      final response = await http.get(
        Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
      );

      if (response.statusCode == 200) {
        // 请求成功,解析JSON数据
        final data = json.decode(response.body);
        print('Response: $data');
      } else {
        // 请求失败
        print('Request failed with status: ${response.statusCode}');
      }
    } catch (e) {
      print('Error occurred: $e');
    }
  }
}

GET请求详解

简单GET请求

dart
class GetRequestDemo extends StatefulWidget {
  @override
  _GetRequestDemoState createState() => _GetRequestDemoState();
}

class _GetRequestDemoState extends State<GetRequestDemo> {
  String _response = '';
  bool _isLoading = false;

  Future<void> _fetchData() async {
    setState(() {
      _isLoading = true;
      _response = '';
    });

    try {
      final response = await http.get(
        Uri.parse('https://jsonplaceholder.typicode.com/users'),
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
      );

      if (response.statusCode == 200) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Error: ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Exception: $e';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GET请求示例')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            ElevatedButton(
              onPressed: _fetchData,
              child: Text('获取用户数据'),
            ),
            SizedBox(height: 20),
            if (_isLoading)
              CircularProgressIndicator()
            else
              Expanded(
                child: SingleChildScrollView(
                  child: Text(_response),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

带参数的GET请求

dart
class GetWithParamsDemo extends StatefulWidget {
  @override
  _GetWithParamsDemoState createState() => _GetWithParamsDemoState();
}

class _GetWithParamsDemoState extends State<GetWithParamsDemo> {
  String _response = '';
  bool _isLoading = false;
  String _userId = '1';

  Future<void> _fetchUserPosts() async {
    setState(() {
      _isLoading = true;
      _response = '';
    });

    try {
      final url = Uri.https('jsonplaceholder.typicode.com', '/posts', {
        'userId': _userId,
      });

      final response = await http.get(url);

      if (response.statusCode == 200) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Error: ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Exception: $e';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('带参数GET请求')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              decoration: InputDecoration(
                labelText: '用户ID',
                hintText: '输入用户ID',
              ),
              onChanged: (value) => _userId = value,
              keyboardType: TextInputType.number,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _fetchUserPosts,
              child: Text('获取用户文章'),
            ),
            SizedBox(height: 20),
            if (_isLoading)
              CircularProgressIndicator()
            else
              Expanded(
                child: SingleChildScrollView(
                  child: Text(_response),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

POST请求详解

基础POST请求

dart
class PostRequestDemo extends StatefulWidget {
  @override
  _PostRequestDemoState createState() => _PostRequestDemoState();
}

class _PostRequestDemoState extends State<PostRequestDemo> {
  String _response = '';
  bool _isLoading = false;
  String _title = '';
  String _body = '';

  Future<void> _createPost() async {
    setState(() {
      _isLoading = true;
      _response = '';
    });

    try {
      final response = await http.post(
        Uri.parse('https://jsonplaceholder.typicode.com/posts'),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: jsonEncode(<String, String>{
          'title': _title,
          'body': _body,
          'userId': '1',
        }),
      );

      if (response.statusCode == 201) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Error: ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Exception: $e';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('POST请求示例')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              decoration: InputDecoration(
                labelText: '标题',
                hintText: '输入文章标题',
              ),
              onChanged: (value) => _title = value,
            ),
            SizedBox(height: 10),
            TextField(
              decoration: InputDecoration(
                labelText: '内容',
                hintText: '输入文章内容',
              ),
              maxLines: 4,
              onChanged: (value) => _body = value,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _createPost,
              child: Text('创建文章'),
            ),
            SizedBox(height: 20),
            if (_isLoading)
              CircularProgressIndicator()
            else if (_response.isNotEmpty)
              Expanded(
                child: Card(
                  child: Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text(_response),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

文件上传

dart
import 'package:http_parser/http_parser.dart';

class FileUploadDemo extends StatefulWidget {
  @override
  _FileUploadDemoState createState() => _FileUploadDemoState();
}

class _FileUploadDemoState extends State<FileUploadDemo> {
  String _response = '';
  bool _isLoading = false;

  Future<void> _uploadFile() async {
    setState(() {
      _isLoading = true;
      _response = '';
    });

    try {
      // 注意:这只是一个模拟示例,实际文件上传需要使用image_picker等插件
      var request = http.MultipartRequest(
        'POST',
        Uri.parse('https://httpbin.org/post'),
      );

      // 添加文本字段
      request.fields['description'] = 'This is a test file upload';
      request.fields['user_id'] = '123';

      // 创建一个模拟文件
      var multipartFile = http.MultipartFile.fromString(
        'file',
        'Sample file content',
        filename: 'sample.txt',
        contentType: MediaType('text', 'plain'),
      );

      request.files.add(multipartFile);

      final streamedResponse = await request.send();
      final response = await http.Response.fromStream(streamedResponse);

      if (response.statusCode == 200) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Error: ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Exception: $e';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('文件上传示例')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            ElevatedButton(
              onPressed: _uploadFile,
              child: Text('上传文件'),
            ),
            SizedBox(height: 20),
            if (_isLoading)
              CircularProgressIndicator()
            else if (_response.isNotEmpty)
              Expanded(
                child: Card(
                  child: Padding(
                    padding: EdgeInsets.all(16.0),
                    child: SingleChildScrollView(
                      child: Text(_response),
                    ),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

数据模型与序列化

创建数据模型

dart
// 用户模型
class User {
  final int id;
  final String name;
  final String username;
  final String email;
  final Address address;
  final String phone;
  final String website;
  final Company company;

  User({
    required this.id,
    required this.name,
    required this.username,
    required this.email,
    required this.address,
    required this.phone,
    required this.website,
    required this.company,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      username: json['username'],
      email: json['email'],
      address: Address.fromJson(json['address']),
      phone: json['phone'],
      website: json['website'],
      company: Company.fromJson(json['company']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'username': username,
      'email': email,
      'address': address.toJson(),
      'phone': phone,
      'website': website,
      'company': company.toJson(),
    };
  }
}

class Address {
  final String street;
  final String suite;
  final String city;
  final String zipcode;
  final Geo geo;

  Address({
    required this.street,
    required this.suite,
    required this.city,
    required this.zipcode,
    required this.geo,
  });

  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      street: json['street'],
      suite: json['suite'],
      city: json['city'],
      zipcode: json['zipcode'],
      geo: Geo.fromJson(json['geo']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'street': street,
      'suite': suite,
      'city': city,
      'zipcode': zipcode,
      'geo': geo.toJson(),
    };
  }
}

class Geo {
  final String lat;
  final String lng;

  Geo({required this.lat, required this.lng});

  factory Geo.fromJson(Map<String, dynamic> json) {
    return Geo(
      lat: json['lat'],
      lng: json['lng'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'lat': lat,
      'lng': lng,
    };
  }
}

class Company {
  final String name;
  final String catchPhrase;
  final String bs;

  Company({required this.name, required this.catchPhrase, required this.bs});

  factory Company.fromJson(Map<String, dynamic> json) {
    return Company(
      name: json['name'],
      catchPhrase: json['catchPhrase'],
      bs: json['bs'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'catchPhrase': catchPhrase,
      'bs': bs,
    };
  }
}

// 使用模型的网络请求
class ModelBasedNetworking extends StatefulWidget {
  @override
  _ModelBasedNetworkingState createState() => _ModelBasedNetworkingState();
}

class _ModelBasedNetworkingState extends State<ModelBasedNetworking> {
  List<User> _users = [];
  bool _isLoading = false;

  Future<void> _fetchUsers() async {
    setState(() {
      _isLoading = true;
      _users = [];
    });

    try {
      final response = await http.get(
        Uri.parse('https://jsonplaceholder.typicode.com/users'),
      );

      if (response.statusCode == 200) {
        final List<dynamic> data = json.decode(response.body);
        final users = data.map((json) => User.fromJson(json)).toList();
        
        setState(() {
          _users = users;
          _isLoading = false;
        });
      } else {
        setState(() {
          _isLoading = false;
        });
        throw Exception('Failed to load users');
      }
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      print('Error: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('模型化网络请求')),
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.all(16.0),
            child: ElevatedButton(
              onPressed: _fetchUsers,
              child: Text('获取用户数据'),
            ),
          ),
          if (_isLoading)
            LinearProgressIndicator()
          else
            Expanded(
              child: ListView.builder(
                itemCount: _users.length,
                itemBuilder: (context, index) {
                  final user = _users[index];
                  return Card(
                    child: Padding(
                      padding: EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            user.name,
                            style: Theme.of(context).textTheme.headlineSmall,
                          ),
                          SizedBox(height: 8),
                          Text('用户名: ${user.username}'),
                          Text('邮箱: ${user.email}'),
                          Text('电话: ${user.phone}'),
                          Text('公司: ${user.company.name}'),
                        ],
                      ),
                    ),
                  );
                },
              ),
            ),
        ],
      ),
    );
  }
}

网络状态管理

创建网络状态管理器

dart
import 'package:flutter/foundation.dart';

enum NetworkStatus { loading, success, error, empty }

class NetworkManager extends ChangeNotifier {
  NetworkStatus _status = NetworkStatus.empty;
  String? _errorMessage;
  dynamic _data;

  NetworkStatus get status => _status;
  String? get errorMessage => _errorMessage;
  dynamic get data => _data;

  Future<void> fetchData(String url) async {
    _status = NetworkStatus.loading;
    _errorMessage = null;
    _data = null;
    notifyListeners();

    try {
      final response = await http.get(Uri.parse(url));

      if (response.statusCode == 200) {
        _status = NetworkStatus.success;
        _data = json.decode(response.body);
      } else {
        _status = NetworkStatus.error;
        _errorMessage = 'HTTP Error: ${response.statusCode}';
      }
    } catch (e) {
      _status = NetworkStatus.error;
      _errorMessage = e.toString();
    }

    notifyListeners();
  }
}

class NetworkStatusDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => NetworkManager(),
      child: Scaffold(
        appBar: AppBar(title: Text('网络状态管理')),
        body: Consumer<NetworkManager>(
          builder: (context, networkManager, child) {
            switch (networkManager.status) {
              case NetworkStatus.loading:
                return Center(child: CircularProgressIndicator());
              
              case NetworkStatus.success:
                final users = networkManager.data as List;
                return RefreshIndicator(
                  onRefresh: () => networkManager.fetchData('https://jsonplaceholder.typicode.com/users'),
                  child: ListView.builder(
                    itemCount: users.length,
                    itemBuilder: (context, index) {
                      final user = users[index];
                      return Card(
                        child: ListTile(
                          title: Text(user['name']),
                          subtitle: Text(user['email']),
                          trailing: Icon(Icons.arrow_forward_ios),
                        ),
                      );
                    },
                  ),
                );
              
              case NetworkStatus.error:
                return Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(Icons.error_outline, size: 64, color: Colors.red),
                      SizedBox(height: 16),
                      Text(
                        '错误: ${networkManager.errorMessage}',
                        textAlign: TextAlign.center,
                      ),
                      SizedBox(height: 16),
                      ElevatedButton(
                        onPressed: () => networkManager.fetchData('https://jsonplaceholder.typicode.com/users'),
                        child: Text('重试'),
                      ),
                    ],
                  ),
                );
              
              case NetworkStatus.empty:
              default:
                return Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(Icons.cloud_outlined, size: 64),
                      SizedBox(height: 16),
                      Text('点击下方按钮获取数据'),
                      SizedBox(height: 16),
                      ElevatedButton(
                        onPressed: () => networkManager.fetchData('https://jsonplaceholder.typicode.com/users'),
                        child: Text('获取数据'),
                      ),
                    ],
                  ),
                );
            }
          },
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            Provider.of<NetworkManager>(context, listen: false)
                .fetchData('https://jsonplaceholder.typicode.com/users');
          },
          child: Icon(Icons.refresh),
        ),
      ),
    );
  }
}

高级网络功能

请求拦截器和配置

dart
class NetworkService {
  static const String _baseUrl = 'https://jsonplaceholder.typicode.com';
  final http.Client _client = http.Client();
  
  // 默认请求头
  Map<String, String> get _defaultHeaders {
    return {
      'Content-Type': 'application/json; charset=UTF-8',
      'Accept': 'application/json',
    };
  }

  // GET请求
  Future<http.Response> get(String endpoint, {Map<String, String>? headers}) async {
    final url = Uri.parse('$_baseUrl$endpoint');
    final requestHeaders = {..._defaultHeaders, ...?headers};
    
    return await _client.get(url, headers: requestHeaders);
  }

  // POST请求
  Future<http.Response> post(String endpoint, dynamic body, {Map<String, String>? headers}) async {
    final url = Uri.parse('$_baseUrl$endpoint');
    final requestHeaders = {..._defaultHeaders, ...?headers};
    
    return await _client.post(
      url,
      headers: requestHeaders,
      body: jsonEncode(body),
    );
  }

  // PUT请求
  Future<http.Response> put(String endpoint, dynamic body, {Map<String, String>? headers}) async {
    final url = Uri.parse('$_baseUrl$endpoint');
    final requestHeaders = {..._defaultHeaders, ...?headers};
    
    return await _client.put(
      url,
      headers: requestHeaders,
      body: jsonEncode(body),
    );
  }

  // DELETE请求
  Future<http.Response> delete(String endpoint, {Map<String, String>? headers}) async {
    final url = Uri.parse('$_baseUrl$endpoint');
    final requestHeaders = {..._defaultHeaders, ...?headers};
    
    return await _client.delete(url, headers: requestHeaders);
  }

  // 关闭客户端
  void close() {
    _client.close();
  }
}

class NetworkServiceDemo extends StatefulWidget {
  @override
  _NetworkServiceDemoState createState() => _NetworkServiceDemoState();
}

class _NetworkServiceDemoState extends State<NetworkServiceDemo> {
  final NetworkService _networkService = NetworkService();
  String _response = '';
  bool _isLoading = false;

  Future<void> _testGet() async {
    setState(() {
      _isLoading = true;
      _response = '';
    });

    try {
      final response = await _networkService.get('/posts/1');
      
      if (response.statusCode == 200) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Error: ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Exception: $e';
        _isLoading = false;
      });
    }
  }

  Future<void> _testPost() async {
    setState(() {
      _isLoading = true;
      _response = '';
    });

    try {
      final response = await _networkService.post(
        '/posts',
        {
          'title': 'Test Post',
          'body': 'This is a test post created via NetworkService',
          'userId': 1,
        },
      );
      
      if (response.statusCode == 201) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Error: ${response.statusCode}';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Exception: $e';
        _isLoading = false;
      });
    }
  }

  @override
  void dispose() {
    _networkService.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('网络服务封装')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: _testGet,
                  child: Text('GET请求'),
                ),
                ElevatedButton(
                  onPressed: _testPost,
                  child: Text('POST请求'),
                ),
              ],
            ),
            SizedBox(height: 20),
            if (_isLoading)
              LinearProgressIndicator()
            else
              Expanded(
                child: Card(
                  child: Padding(
                    padding: EdgeInsets.all(16.0),
                    child: SingleChildScrollView(
                      child: Text(_response),
                    ),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

网络缓存

实现简单的HTTP缓存

dart
import 'package:shared_preferences/shared_preferences.dart';

class CachedNetworkService {
  final NetworkService _networkService = NetworkService();
  final Map<String, CacheEntry> _cache = {};
  static const Duration _cacheDuration = Duration(minutes: 5);

  Future<Map<String, dynamic>?> getCachedData(String endpoint) async {
    // 检查内存缓存
    final cacheEntry = _cache[endpoint];
    if (cacheEntry != null && !cacheEntry.isExpired) {
      print('Cache hit for: $endpoint');
      return cacheEntry.data;
    }

    // 检查本地缓存
    final prefs = await SharedPreferences.getInstance();
    final cachedJson = prefs.getString(endpoint);
    final cachedTime = prefs.getInt('${endpoint}_time');

    if (cachedJson != null && cachedTime != null) {
      final cacheDateTime = DateTime.fromMillisecondsSinceEpoch(cachedTime);
      if (DateTime.now().difference(cacheDateTime) < _cacheDuration) {
        final data = json.decode(cachedJson) as Map<String, dynamic>;
        // 更新内存缓存
        _cache[endpoint] = CacheEntry(data, DateTime.now().add(_cacheDuration));
        print('Local cache hit for: $endpoint');
        return data;
      } else {
        // 清除过期缓存
        await prefs.remove(endpoint);
        await prefs.remove('${endpoint}_time');
      }
    }

    // 发起网络请求
    try {
      final response = await _networkService.get(endpoint);
      if (response.statusCode == 200) {
        final data = json.decode(response.body) as Map<String, dynamic>;
        
        // 存储到内存缓存
        _cache[endpoint] = CacheEntry(data, DateTime.now().add(_cacheDuration));
        
        // 存储到本地缓存
        await prefs.setString(endpoint, json.encode(data));
        await prefs.setInt('${endpoint}_time', DateTime.now().millisecondsSinceEpoch);
        
        print('Network request and cache for: $endpoint');
        return data;
      }
    } catch (e) {
      print('Network request failed: $e');
    }

    return null;
  }

  void clearCache() {
    _cache.clear();
  }
}

class CacheEntry {
  final Map<String, dynamic> data;
  final DateTime expiryTime;

  CacheEntry(this.data, this.expiryTime);

  bool get isExpired => DateTime.now().isAfter(expiryTime);
}

class CachedNetworkingDemo extends StatefulWidget {
  @override
  _CachedNetworkingDemoState createState() => _CachedNetworkingDemoState();
}

class _CachedNetworkingDemoState extends State<CachedNetworkingDemo> {
  final CachedNetworkService _cacheService = CachedNetworkService();
  String _response = '';
  bool _isLoading = false;

  Future<void> _fetchData() async {
    setState(() {
      _isLoading = true;
      _response = 'Loading...';
    });

    try {
      final data = await _cacheService.getCachedData('/posts/1');
      if (data != null) {
        setState(() {
          _response = json.encode(data, toEncodable: (dynamic obj) => obj.toString());
          _isLoading = false;
        });
      } else {
        setState(() {
          _response = 'Failed to load data';
          _isLoading = false;
        });
      }
    } catch (e) {
      setState(() {
        _response = 'Error: $e';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('网络缓存示例')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: _fetchData,
                  child: Text('获取数据(带缓存)'),
                ),
                ElevatedButton(
                  onPressed: () {
                    _cacheService.clearCache();
                    setState(() {
                      _response = '缓存已清除';
                    });
                  },
                  child: Text('清除缓存'),
                ),
              ],
            ),
            SizedBox(height: 20),
            if (_isLoading)
              CircularProgressIndicator()
            else
              Expanded(
                child: Card(
                  child: Padding(
                    padding: EdgeInsets.all(16.0),
                    child: SingleChildScrollView(
                      child: Text(_response),
                    ),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

网络错误处理

全面的错误处理机制

dart
class NetworkError {
  final String message;
  final int? statusCode;
  final Exception? exception;

  NetworkError({
    required this.message,
    this.statusCode,
    this.exception,
  });

  factory NetworkError.fromException(Exception e) {
    if (e is http.ClientException) {
      return NetworkError(
        message: '网络连接错误,请检查网络设置',
        exception: e,
      );
    } else if (e is FormatException) {
      return NetworkError(
        message: '数据格式错误',
        exception: e,
      );
    } else if (e is TimeoutException) {
      return NetworkError(
        message: '请求超时,请稍后重试',
        exception: e,
      );
    } else {
      return NetworkError(
        message: '未知错误: ${e.toString()}',
        exception: e,
      );
    }
  }

  factory NetworkError.fromStatusCode(int statusCode) {
    String message;
    switch (statusCode) {
      case 400:
        message = '请求参数错误';
        break;
      case 401:
        message = '未授权访问';
        break;
      case 403:
        message = '禁止访问';
        break;
      case 404:
        message = '资源未找到';
        break;
      case 500:
        message = '服务器内部错误';
        break;
      case 502:
        message = '网关错误';
        break;
      case 503:
        message = '服务不可用';
        break;
      default:
        message = 'HTTP错误 ($statusCode)';
    }
    return NetworkError(
      message: message,
      statusCode: statusCode,
    );
  }
}

class ErrorHandlingDemo extends StatefulWidget {
  @override
  _ErrorHandlingDemoState createState() => _ErrorHandlingDemoState();
}

class _ErrorHandlingDemoState extends State<ErrorHandlingDemo> {
  String _response = '';
  bool _isLoading = false;
  NetworkError? _error;

  Future<void> _fetchWithErrorHandling() async {
    setState(() {
      _isLoading = true;
      _response = '';
      _error = null;
    });

    try {
      final response = await http.get(
        Uri.parse('https://jsonplaceholder.typicode.com/posts/999999'),
        headers: {'Content-Type': 'application/json'},
      );

      if (response.statusCode == 200) {
        setState(() {
          _response = response.body;
          _isLoading = false;
        });
      } else {
        final error = NetworkError.fromStatusCode(response.statusCode);
        setState(() {
          _error = error;
          _isLoading = false;
        });
      }
    } catch (e) {
      final error = NetworkError.fromException(e as Exception);
      setState(() {
        _error = error;
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('错误处理示例')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            ElevatedButton(
              onPressed: _fetchWithErrorHandling,
              child: Text('发起可能出错的请求'),
            ),
            SizedBox(height: 20),
            if (_isLoading)
              CircularProgressIndicator()
            else if (_error != null)
              Card(
                color: Colors.red.shade50,
                child: Padding(
                  padding: EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      Icon(Icons.error, color: Colors.red, size: 48),
                      SizedBox(height: 16),
                      Text(
                        '错误信息',
                        style: Theme.of(context).textTheme.headlineSmall,
                      ),
                      SizedBox(height: 8),
                      Text(
                        _error!.message,
                        style: TextStyle(fontSize: 16),
                      ),
                      if (_error!.statusCode != null)
                        Text('状态码: ${_error!.statusCode}'),
                      SizedBox(height: 16),
                      ElevatedButton(
                        onPressed: _fetchWithErrorHandling,
                        child: Text('重试'),
                      ),
                    ],
                  ),
                ),
              )
            else if (_response.isNotEmpty)
              Expanded(
                child: Card(
                  child: Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Text(_response),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

网络状态监测

监测设备网络状态

dart
import 'package:connectivity_plus/connectivity_plus.dart';

class NetworkStatusMonitor extends StatefulWidget {
  @override
  _NetworkStatusMonitorState createState() => _NetworkStatusMonitorState();
}

class _NetworkStatusMonitorState extends State<NetworkStatusMonitor> {
  ConnectivityResult _connectionStatus = ConnectivityResult.none;
  String _statusMessage = '未知网络状态';
  StreamSubscription<ConnectivityResult>? _connectivitySubscription;

  @override
  void initState() {
    super.initState();
    _checkConnection();
    _listenConnectionChanges();
  }

  Future<void> _checkConnection() async {
    final result = await Connectivity().checkConnectivity();
    setState(() {
      _connectionStatus = result;
      _statusMessage = _getConnectionMessage(result);
    });
  }

  void _listenConnectionChanges() {
    _connectivitySubscription = Connectivity().onConnectivityChanged.listen((result) {
      setState(() {
        _connectionStatus = result;
        _statusMessage = _getConnectionMessage(result);
      });
    });
  }

  String _getConnectionMessage(ConnectivityResult result) {
    switch (result) {
      case ConnectivityResult.wifi:
        return '已连接到WiFi网络';
      case ConnectivityResult.mobile:
        return '已连接到移动数据网络';
      case ConnectivityResult.none:
        return '无网络连接';
      default:
        return '未知网络状态';
    }
  }

  Color _getConnectionColor(ConnectivityResult result) {
    switch (result) {
      case ConnectivityResult.wifi:
        return Colors.green;
      case ConnectivityResult.mobile:
        return Colors.blue;
      case ConnectivityResult.none:
        return Colors.red;
      default:
        return Colors.grey;
    }
  }

  @override
  void dispose() {
    _connectivitySubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('网络状态监测')),
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(
                _connectionStatus == ConnectivityResult.none
                    ? Icons.signal_wifi_off
                    : Icons.network_check,
                size: 80,
                color: _getConnectionColor(_connectionStatus),
              ),
              SizedBox(height: 20),
              Text(
                _statusMessage,
                style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                      color: _getConnectionColor(_connectionStatus),
                    ),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: _checkConnection,
                child: Text('重新检查网络状态'),
              ),
              SizedBox(height: 20),
              if (_connectionStatus != ConnectivityResult.none)
                ElevatedButton(
                  onPressed: () async {
                    try {
                      final response = await http
                          .get(Uri.parse('https://httpbin.org/get'))
                          .timeout(Duration(seconds: 5));
                      
                      if (response.statusCode == 200) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(
                            content: Text('网络连接正常'),
                            backgroundColor: Colors.green,
                          ),
                        );
                      }
                    } catch (e) {
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(
                          content: Text('网络连接测试失败: $e'),
                          backgroundColor: Colors.red,
                        ),
                      );
                    }
                  },
                  child: Text('测试网络连通性'),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

网络请求最佳实践

1. 请求超时和重试机制

dart
class RobustNetworkService {
  static Future<http.Response> getWithTimeout(String url, {Duration timeout = const Duration(seconds: 10)}) async {
    try {
      final response = await http.get(Uri.parse(url)).timeout(timeout);
      return response;
    } on TimeoutException {
      throw Exception('请求超时');
    } on Exception {
      rethrow;
    }
  }

  static Future<http.Response> getWithRetry(String url, {int maxRetries = 3, Duration delay = const Duration(seconds: 1)}) async {
    http.Response? lastResponse;
    Exception? lastException;

    for (int attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        final response = await http.get(Uri.parse(url));
        if (response.statusCode >= 200 && response.statusCode < 300) {
          return response;
        }
        lastResponse = response;
      } on Exception catch (e) {
        lastException = e;
      }

      if (attempt < maxRetries) {
        await Future.delayed(delay);
      }
    }

    if (lastResponse != null) {
      return lastResponse;
    } else if (lastException != null) {
      throw lastException;
    } else {
      throw Exception('未知错误');
    }
  }
}

2. 网络请求拦截器

dart
class InterceptorNetworkService {
  final http.Client _client = http.Client();
  final List<RequestInterceptor> _requestInterceptors = [];
  final List<ResponseInterceptor> _responseInterceptors = [];

  void addRequestInterceptor(RequestInterceptor interceptor) {
    _requestInterceptors.add(interceptor);
  }

  void addResponseInterceptor(ResponseInterceptor interceptor) {
    _responseInterceptors.add(interceptor);
  }

  Future<http.Response> get(String url) async {
    var uri = Uri.parse(url);
    
    // 应用请求拦截器
    for (var interceptor in _requestInterceptors) {
      uri = await interceptor.interceptUri(uri);
    }

    var response = await _client.get(uri);

    // 应用响应拦截器
    for (var interceptor in _responseInterceptors) {
      response = await interceptor.interceptResponse(response);
    }

    return response;
  }

  void close() {
    _client.close();
  }
}

typedef RequestInterceptorFunc = Future<Uri> Function(Uri uri);
typedef ResponseInterceptorFunc = Future<http.Response> Function(http.Response response);

class RequestInterceptor {
  final RequestInterceptorFunc interceptUri;

  RequestInterceptor(this.interceptUri);
}

class ResponseInterceptor {
  final ResponseInterceptorFunc interceptResponse;

  ResponseInterceptor(this.interceptResponse);
}

总结

Flutter网络编程涉及多个重要方面:

  1. 基础网络请求:使用http包进行GET、POST等基本请求
  2. 数据模型:创建对应API响应的数据模型并实现序列化
  3. 状态管理:管理网络请求的加载、成功、错误等状态
  4. 高级功能:实现请求拦截、缓存、错误处理等
  5. 网络监测:监测设备网络状态以提供更好的用户体验
  6. 最佳实践:实现超时、重试、拦截器等健壮性功能

通过合理运用这些网络编程技术,可以构建高效、可靠、用户体验良好的Flutter应用。