Flutter网络功能插件flutter_feature_network的使用

Flutter网络功能插件flutter_feature_network的使用

flutter_feature_network 是一个为简化和增强 Flutter 应用程序中的网络操作而设计的库。该库提供了一套工具和方法来高效地管理网络请求,包括设置 Dio 客户端、日志记录、请求标识以及 SSL 安全性。

方法

获取 Dio 客户端

生成用于 HTTP 事务的 Dio 客户端。

final dioClient = FeatureNetworkRepositoryImpl().getDioClient();
// 或者
final dioClient = FlutterFeatureNetwork.getDioClient();

参数说明

参数名称 类型 是否必需 描述
receiveTimeout Duration 客户端从服务器接收数据的最大等待时间。
connectTimeout Duration 客户端建立连接到服务器的最大允许时间。
sendTimeout Duration 客户端发送请求数据到服务器的最大允许时间。
baseUrl String 所有请求的基础 URL。
headers Map<String, String> 每个请求包含的默认头信息。
interceptors List<Interceptor> 要添加到 Dio 客户端的拦截器列表,用于修改请求/响应。
trustedCertificateBytes List<int> 表示受信任证书的字节列表,用于 SSL 针扎。仅允许 trustedCertificateBytesallowedFingerprints 中的一个。
allowedFingerprints List<String> 允许的 SSL 证书 SHA 指纹列表,用于安全连接。仅允许 trustedCertificateBytesallowedFingerprints 中的一个。

检查连接是否安全

检查使用证书的连接是否安全。

如果连接安全,则返回 true,否则返回 false

final isSecure = FeatureNetworkRepositoryImpl().isConnectionSecure(
  serverUrl: 'https://jsonplaceholder.typicode.com/',
  sha: SHA.SHA_256,
  allowedSHAFingerprints: [
    '14f9996f9481eac7f9c005f6954c2f032d8e9cb13d4440ebed35f14bed22c43f',
  ],
);
// 或者
final isSecure = FlutterFeatureNetwork.isConnectionSecure(
  serverUrl: 'https://jsonplaceholder.typicode.com/',
  sha: SHA.SHA_256,
  allowedSHAFingerprints: [
    '14f9996f9481eac7f9c005f6954c2f032d8e9cb13d4440ebed35f14bed22c43f',
  ],
);

参数说明

参数名称 类型 是否必需 描述
serverUrl String 要检查连接的服务器 URL。
sha SHA 用于证书指纹的哈希算法(例如 SHA_256)。
allowedSHAFingerprints List<String> 允许的 SHA 指纹列表。

检查 HTTP 证书针扎

检查使用证书的连接是否安全。

FeatureNetworkRepositoryImpl().checkHttpCertificatePinning(
  serverUrl: 'https://jsonplaceholder.typicode.com/',
  sha: SHA.SHA_256,
  allowedSHAFingerprints: [
    '14f9996f9481eac7f9c005f6954c2f032d8e9cb13d4440ebed35f14bed22c43f',
  ],
);
// 或者
FlutterFeatureNetwork.checkHttpCertificatePinning(
  serverUrl: 'https://jsonplaceholder.typicode.com/',
  sha: SHA.SHA_256,
  allowedSHAFingerprints: [
    '14f9996f9481eac7f9c005f6954c2f032d8e9cb13d4440ebed35f14bed22c43f',
  ],
);

参数说明

参数名称 类型 是否必需 描述
serverUrl String 要检查连接的服务器 URL。
sha SHA 用于证书指纹的哈希算法(例如 SHA_256)。
allowedSHAFingerprints List<String> 允许的 SHA 指纹列表。

插件 FlutterFeatureNetwork

通过资源获取 HTTP 证书字节

final certificateBytes = FlutterFeatureNetwork.getCertificateBytesFromAsset(assetPath: 'asset/certificate.pem');

参数说明

参数名称 类型 是否必需 描述
assetPath String 资源路径。

其他

Alice

Alice 是一个用于 Flutter 的 HTTP Inspector 工具,帮助调试 HTTP 请求。它捕获并存储 HTTP 请求和响应,可以通过简单的用户界面查看。

// 设置 Alice
final alice = Alice(showNotification: true, showInspectorOnShake: true);

// 在 Material App 中使用
[@override](/user/override)
Widget build(BuildContext context) {
  return MaterialApp(
    navigatorKey: alice.getNavigatorKey(),
    title: 'Flutter Feature Network',
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueAccent),
      useMaterial3: true,
    ),
    home: const MainPage(),
  );
}

日志拦截器

日志拦截器是一个用于在终端中调试请求和响应的拦截器。它帮助开发人员调试 HTTP。

final dio = FeatureNetworkRepositoryImpl().getDioClient(
  // ...
  interceptors: [
    LoggerInterceptor(),
  ],
  // ...
);
// 或者
final dio = FlutterFeatureNetwork.getDioClient(
  // ...
  interceptors: [
    LoggerInterceptor(),
  ],
  // ...
);

示例代码

import 'dart:io';

import 'package:example/data/repository/repository_datasource.dart';
import 'package:example/data/state/fetch_network_state.dart';
import 'package:example/domain/interceptor/dynamic_ssl_interceptor.dart';
import 'package:example/domain/interceptor/example_ssl_interceptor.dart';
import 'package:example/firebase_options.dart';
import 'package:example/presentation/main_store.dart';
import 'package:example/presentation/widget/feature_widget.dart';
import 'package:example/presentation/widget/info_bottomsheet.dart';
import 'package:example/presentation/widget/loading_dialog.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feature_network/flutter_feature_network.dart';
import 'package:get_it/get_it.dart';
import 'package:flutter_feature_platform/flutter_feature_platform.dart';
import 'package:mobx/mobx.dart';

import 'data/dto/model/feature_model.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Alice alice;
  late FirebaseRemoteConfig remoteConfig;
  bool isAllFullySetup = false;
  late Dio placeHolderStandardDio;
  late Dio sslDio;

  [@override](/user/override)
  void initState() {
    super.initState();
    FeatureNetworkRepository networkRepository = FeatureNetworkRepositoryImpl();
    GetIt.I.registerFactory<FeatureNetworkRepository>(() => networkRepository);
    GetIt.I.registerFactory<FeaturePlatformRepository>(() => FeaturePlatformRepositoryImpl());
    alice = Alice(showNotification: true, showInspectorOnShake: true);
    GetIt.I.registerSingleton(alice);
    remoteConfig = FirebaseRemoteConfig.instance;
    remoteConfig.setConfigSettings(RemoteConfigSettings(
      fetchTimeout: const Duration(seconds: 60),
      minimumFetchInterval: const Duration(seconds: 10),
    ));
    GetIt.I.registerSingleton(remoteConfig);
    GetIt.I.get<FirebaseRemoteConfig>().fetchAndActivate();
    GetIt.I.get<FirebaseRemoteConfig>().ensureInitialized();

    Future.delayed(const Duration(seconds: 3), () {
      setState(() {
        isAllFullySetup = true;
      });
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return isAllFullySetup
        ? MaterialApp(
            navigatorKey: alice.getNavigatorKey(),
            title: 'Flutter Feature Network',
            theme: ThemeData(
              colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueAccent),
              useMaterial3: true,
            ),
            home: const MainPage(),
          )
        : MaterialApp(
            title: 'Flutter Feature Network - Non Alice',
            theme: ThemeData(
              colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
              useMaterial3: true,
            ),
            home: const SizedBox.shrink(),
          );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  [@override](/user/override)
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  late MainStore mainStore;
  bool isInitialized = false;
  List<FeatureModel> features = [
    FeatureModel(
      title: 'Fetched Post',
      desc: 'Fetched Post - OK',
      key: 'FETCHED_POST_OK',
    ),
    FeatureModel(
      title: 'Fetched Post',
      desc: 'Fetched Post - Correct Fingerprint',
      key: 'FETCHED_POST_CORRECT_FINGERPRINT',
    ),
    FeatureModel(
      title: 'Fetched Post',
      desc: 'Fetched Post - Incorrect Fingerprint',
      key: 'FETCHED_POST_INCORRECT_FINGERPRINT',
    ),
    FeatureModel(
      title: 'Fetched Post',
      desc: 'Fetched Post - Dynamic Fingerprint',
      key: 'FETCHED_POST_DYNAMIC_FINGERPRINT',
    ),
    FeatureModel(
      title: 'Fetched Post',
      desc: 'Fetched Post - Correct Certificate Byte',
      key: 'FETCHED_POST_CORRECT_CERTIFICATE_BYTE',
    ),
    FeatureModel(
      title: 'Fetched Post',
      desc: 'Fetched Post - Incorrect Certificate Byte',
      key: 'FETCHED_POST_INCORRECT_CERTIFICATE_BYTE',
    ),
  ];
  List<ReactionDisposer> reactions = [];

  [@override](/user/override)
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(
      (_) {
        init();
      },
    );
  }

  Future<void> init() async {
    final userAgent = await GetIt.I.get<FeaturePlatformRepository>().getUserAgent();
    final placeHolderStandardDio = GetIt.I.get<FeatureNetworkRepository>().getDioClient();
    final placeHolderCorrectFingerprintDio = GetIt.I.get<FeatureNetworkRepository>().getDioClient(
      baseUrl: 'https://jsonplaceholder.typicode.com/',
      headers: {
        HttpHeaders.userAgentHeader: userAgent,
      },
      interceptors: [
        LoggerInterceptor(),
        GetIt.I.get<Alice>().getDioInterceptor(),
      ],
      allowedFingerprints: [
        '14f9996f9481eac7f9c005f6954c2f032d8e9cb13d4440ebed35f14bed22c43f',
      ],
    );
    final placeHolderIncorrectFingerprintDio = GetIt.I.get<FeatureNetworkRepository>().getDioClient(
      baseUrl: 'https://jsonplaceholder.typicode.com/',
      headers: {
        HttpHeaders.userAgentHeader: userAgent,
      },
      interceptors: [
        GetIt.I.get<Alice>().getDioInterceptor(),
        LoggerInterceptor(),
      ],
      allowedFingerprints: [
        '065e3b66390a5d3c7ce51f27342442606453b3d98e4d4e97f5b708b59d190a0a',
      ],
    );
    final placeHolderDynamicSslFingerprintDio = GetIt.I.get<FeatureNetworkRepository>().getDioClient(
      baseUrl: 'https://jsonplaceholder.typicode.com/',
      headers: {
        HttpHeaders.userAgentHeader: userAgent,
      },
      interceptors: [
        DynamicSslInterceptor(remoteConfig: GetIt.I.get<FirebaseRemoteConfig>()),
        GetIt.I.get<Alice>().getDioInterceptor(),
        LoggerInterceptor(),
      ],
    );
    final jsonPlaceholderCertByte =
        await FlutterFeatureNetwork.getCertificateBytesFromAsset(assetPath: 'assets/jsonplaceholder_cert.pem');
    final wikipediaCertByte =
        await FlutterFeatureNetwork.getCertificateBytesFromAsset(assetPath: 'assets/wikipedia_cert.pem');
    final correctCertificateByteDio = GetIt.I.get<FeatureNetworkRepository>().getDioClient(
          baseUrl: 'https://jsonplaceholder.typicode.com/',
          headers: {
            HttpHeaders.userAgentHeader: userAgent,
          },
          interceptors: [
            GetIt.I.get<Alice>().getDioInterceptor(),
            LoggerInterceptor(),
          ],
          trustedCertificateBytes: jsonPlaceholderCertByte,
        );
    final incorrectCertificateByteDio = FlutterFeatureNetwork.getDioClient(
          baseUrl: 'https://jsonplaceholder.typicode.com/',
          headers: {
            HttpHeaders.userAgentHeader: userAgent,
          },
          interceptors: [
            ExampleSSLInterceptor(),
            GetIt.I.get<Alice>().getDioInterceptor(),
            LoggerInterceptor(),
          ],
          trustedCertificateBytes: wikipediaCertByte,
        );
    mainStore = MainStore(
      repositoryDatasource: RepositoryDatasourceImpl(
        placeHolderStandardDio: placeHolderStandardDio,
        placeHolderCorrectFingerprintDio: placeHolderCorrectFingerprintDio,
        placeHolderIncorrectFingerprintDio: placeHolderIncorrectFingerprintDio,
        placeHolderDynamicFingerprintDio: placeHolderDynamicSslFingerprintDio,
        placeHolderCorrectCertByteDio: correctCertificateByteDio,
        placeHolderIncorrectCertByteDio: incorrectCertificateByteDio,
      ),
    );
    reactions = [
      reaction((p0) => mainStore.fetchNetworkState, (p0) {
        if (p0 is FetchNetworkLoadingState) {
          showLoading();
        } else if (p0 is FetchNetworkSuccessState) {
          Navigator.pop(context);
          showInfo(title: 'OK', desc: 'Success/Sukses');
        } else if (p0 is FetchNetworkFailedState) {
          Navigator.pop(context);
          showInfo(title: p0.exception.title, desc: p0.exception.desc);
        }
      })
    ];

    setState(() {
      isInitialized = true;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('NETWORK')),
      body: isInitialized
          ? ListView.builder(
              padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
              itemCount: features.length,
              itemBuilder: (_, index) {
                final feature = features[index];
                return GestureDetector(
                  onTap: () async {
                    switch (feature.key) {
                      case "FETCHED_POST_OK":
                        mainStore.getPostById();
                        break;
                      case "FETCHED_POST_CORRECT_FINGERPRINT":
                        mainStore.getPostByIdCorrectFingerprint();
                        break;
                      case "FETCHED_POST_INCORRECT_FINGERPRINT":
                        mainStore.getPostByIdIncorrectFingerprint();
                        break;
                      case "FETCHED_POST_DYNAMIC_FINGERPRINT":
                        mainStore.getPostByIdDynamicFingerprint();
                        break;
                      case "FETCHED_POST_CORRECT_CERTIFICATE_BYTE":
                        mainStore.getPostByIdCorrectCertByte();
                        break;
                      case "FETCHED_POST_INCORRECT_CERTIFICATE_BYTE":
                        mainStore.getPostByIdIncorrectCertByte();
                        break;
                    }
                  },
                  child: ItemFeatureWidget(feature: feature),
                );
              },
            )
          : const Center(
              child: CircularProgressIndicator(),
            ),
    );
  }

  void showLoading() {
    showDialog(
      context: context,
      builder: (_) {
        return const LoadingDialog();
      },
    );
  }

  void showInfo({required String title, required String desc}) {
    showModalBottomSheet(
      shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
      context: context,
      builder: (_) {
        return InfoBottomsheet(title: title, desc: desc);
      },
    );
  }
}

更多关于Flutter网络功能插件flutter_feature_network的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网络功能插件flutter_feature_network的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_feature_network 是一个用于简化 Flutter 应用中网络请求的插件。它提供了一些便捷的方法来处理常见的网络操作,如 GET、POST、PUT、DELETE 请求,以及文件上传和下载等。

安装

首先,你需要在 pubspec.yaml 文件中添加 flutter_feature_network 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_feature_network: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

1. 初始化

在使用 flutter_feature_network 之前,通常需要初始化网络配置。你可以在应用的启动时进行初始化。

import 'package:flutter_feature_network/flutter_feature_network.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 初始化网络配置
  NetworkConfig config = NetworkConfig(
    baseUrl: "https://api.example.com",
    connectTimeout: 5000,
    receiveTimeout: 3000,
  );
  
  NetworkManager().init(config);
  
  runApp(MyApp());
}

2. 发起 GET 请求

import 'package:flutter_feature_network/flutter_feature_network.dart';

Future<void> fetchData() async {
  try {
    Response response = await NetworkManager().get("/users");
    if (response.statusCode == 200) {
      print("Data: ${response.data}");
    } else {
      print("Failed to load data");
    }
  } catch (e) {
    print("Error: $e");
  }
}

3. 发起 POST 请求

import 'package:flutter_feature_network/flutter_feature_network.dart';

Future<void> postData() async {
  try {
    Response response = await NetworkManager().post(
      "/users",
      data: {
        "name": "John Doe",
        "email": "john.doe@example.com",
      },
    );
    if (response.statusCode == 201) {
      print("Data posted successfully");
    } else {
      print("Failed to post data");
    }
  } catch (e) {
    print("Error: $e");
  }
}

4. 发起 PUT 请求

import 'package:flutter_feature_network/flutter_feature_network.dart';

Future<void> updateData() async {
  try {
    Response response = await NetworkManager().put(
      "/users/1",
      data: {
        "name": "Jane Doe",
      },
    );
    if (response.statusCode == 200) {
      print("Data updated successfully");
    } else {
      print("Failed to update data");
    }
  } catch (e) {
    print("Error: $e");
  }
}

5. 发起 DELETE 请求

import 'package:flutter_feature_network/flutter_feature_network.dart';

Future<void> deleteData() async {
  try {
    Response response = await NetworkManager().delete("/users/1");
    if (response.statusCode == 204) {
      print("Data deleted successfully");
    } else {
      print("Failed to delete data");
    }
  } catch (e) {
    print("Error: $e");
  }
}

6. 文件上传

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

Future<void> uploadFile() async {
  try {
    var file = await MultipartFile.fromFile(
      "path/to/file.jpg",
      contentType: MediaType("image", "jpg"),
    );
    
    Response response = await NetworkManager().upload(
      "/upload",
      files: [file],
    );
    
    if (response.statusCode == 200) {
      print("File uploaded successfully");
    } else {
      print("Failed to upload file");
    }
  } catch (e) {
    print("Error: $e");
  }
}

7. 文件下载

import 'package:flutter_feature_network/flutter_feature_network.dart';

Future<void> downloadFile() async {
  try {
    String savePath = "path/to/save/file.jpg";
    await NetworkManager().download("/download/file.jpg", savePath);
    print("File downloaded successfully");
  } catch (e) {
    print("Error: $e");
  }
}

高级配置

flutter_feature_network 还支持一些高级配置,如拦截器、请求头、缓存等。

1. 添加请求头

NetworkManager().addHeaders({
  "Authorization": "Bearer your_token",
  "Content-Type": "application/json",
});

2. 使用拦截器

NetworkManager().addInterceptor(
  (RequestOptions options) {
    print("Request URL: ${options.uri}");
    return options;
  },
  (Response response) {
    print("Response status: ${response.statusCode}");
    return response;
  },
);

3. 缓存配置

NetworkManager().enableCache(
  maxAge: Duration(minutes: 10),
  maxStale: Duration(days: 1),
);
回到顶部