Flutter 小白入门教程Flutter 小白入门教程
首页
学习指南
项目实战
Flutter 官网
编程指南
首页
学习指南
项目实战
Flutter 官网
编程指南
  • 入门基础

    • 📚 基础教程
    • 第1章 - 认识 Flutter
    • 第2章 - 环境搭建
    • 第3章 - Dart 语言基础
    • 第4章 - 第一个 Flutter 应用
    • 第5章 - Widget 基础
    • 第6章 - 布局系统
    • 第7章 - 状态管理入门
    • 第8章 - 页面导航
    • 第9章 - 资源管理
  • 进阶开发

    • 第10章 - 网络请求
    • 第11章 - 本地存储
    • 第12章 - 对话框与反馈
    • 第13章 - 列表进阶
    • 第14章 - 主题定制
    • 第15章 - 状态管理进阶
    • 第16章 - 动画入门
    • 第17章 - 常用第三方包
  • 调试与发布

    • 第18章 - 调试与性能优化
    • 第19章 - 打包与发布
  • 附录

    • 附录A - UI 框架与组件库推荐
    • 附录B - 项目结构最佳实践
    • 附录C - 国际化配置
    • 附录D - 权限处理

第17章 - 常用第三方包

嗨,朋友!我是长安。

Flutter 的生态系统非常丰富,有大量优秀的第三方包可以帮助我们快速开发。这一章,我会介绍在实际项目中最常用的包,让你少走弯路,直接用上最好的工具!

📦 包管理基础

如何添加依赖

在 pubspec.yaml 中添加:

dependencies:
  flutter:
    sdk: flutter
  dio: ^5.4.0        # 网络请求
  provider: ^6.1.1   # 状态管理
  # 更多包...

然后运行:

flutter pub get

在哪里找包?

  • pub.dev - Flutter 官方包仓库
  • 搜索时注意看:
    • Likes - 点赞数越多越好
    • Pub Points - 质量评分(满分 160)
    • Popularity - 使用率
    • 最近更新时间 - 确保还在维护

🌐 网络请求

dio(强烈推荐)

最流行的 HTTP 客户端,功能强大。

dependencies:
  dio: ^5.4.0
import 'package:dio/dio.dart';

final dio = Dio(
  BaseOptions(
    baseUrl: 'https://api.example.com',
    connectTimeout: const Duration(seconds: 10),
    receiveTimeout: const Duration(seconds: 10),
  ),
);

// GET 请求
final response = await dio.get('/users');

// POST 请求
final response = await dio.post('/users', data: {
  'name': '长安',
  'email': 'changan@example.com',
});

// 添加拦截器(统一处理 Token、日志等)
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    options.headers['Authorization'] = 'Bearer $token';
    return handler.next(options);
  },
  onError: (error, handler) {
    if (error.response?.statusCode == 401) {
      // 处理登录过期
    }
    return handler.next(error);
  },
));

retrofit(配合 dio 使用)

声明式 API 定义,类似 Android 的 Retrofit。

dependencies:
  retrofit: ^4.1.0
  dio: ^5.4.0

dev_dependencies:
  retrofit_generator: ^8.1.0
  build_runner: ^2.4.8
  json_serializable: ^6.7.1
import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';

part 'api_client.g.dart';

@RestApi(baseUrl: 'https://api.example.com')
abstract class ApiClient {
  factory ApiClient(Dio dio) = _ApiClient;

  @GET('/users')
  Future<List<User>> getUsers();

  @GET('/users/{id}')
  Future<User> getUser(@Path('id') int id);

  @POST('/users')
  Future<User> createUser(@Body() User user);
}

// 使用
final client = ApiClient(Dio());
final users = await client.getUsers();

运行生成代码:

flutter pub run build_runner build

🖼️ 图片处理

cached_network_image(网络图片缓存)

自动缓存网络图片,避免重复下载。

dependencies:
  cached_network_image: ^3.3.1
import 'package:cached_network_image/cached_network_image.dart';

CachedNetworkImage(
  imageUrl: 'https://example.com/image.jpg',
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
  fit: BoxFit.cover,
  width: 200,
  height: 200,
)

// 圆形头像
CircleAvatar(
  radius: 40,
  backgroundImage: CachedNetworkImageProvider(
    'https://example.com/avatar.jpg',
  ),
)

image_picker(选择图片/拍照)

从相册选择图片或调用相机拍照。

dependencies:
  image_picker: ^1.0.7
import 'package:image_picker/image_picker.dart';

final picker = ImagePicker();

// 从相册选择
final XFile? image = await picker.pickImage(
  source: ImageSource.gallery,
  maxWidth: 1000,
  maxHeight: 1000,
  imageQuality: 80,
);

// 拍照
final XFile? photo = await picker.pickImage(
  source: ImageSource.camera,
);

// 选择多张图片
final List<XFile> images = await picker.pickMultiImage();

// 使用选择的图片
if (image != null) {
  File file = File(image.path);
  // 上传或显示图片
}

权限配置

Android: 在 android/app/src/main/AndroidManifest.xml 添加:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

iOS: 在 ios/Runner/Info.plist 添加:

<key>NSCameraUsageDescription</key>
<string>需要访问相机来拍照</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册来选择图片</string>

image_cropper(图片裁剪)

裁剪图片,常用于头像上传。

dependencies:
  image_cropper: ^5.0.1
import 'package:image_cropper/image_cropper.dart';

final croppedFile = await ImageCropper().cropImage(
  sourcePath: imageFile.path,
  aspectRatio: const CropAspectRatio(ratioX: 1, ratioY: 1),
  uiSettings: [
    AndroidUiSettings(
      toolbarTitle: '裁剪图片',
      toolbarColor: Colors.blue,
      toolbarWidgetColor: Colors.white,
      lockAspectRatio: true,
    ),
    IOSUiSettings(
      title: '裁剪图片',
      aspectRatioLockEnabled: true,
    ),
  ],
);

if (croppedFile != null) {
  // 使用裁剪后的图片
  File file = File(croppedFile.path);
}

📱 UI 组件

flutter_screenutil(屏幕适配)

让 UI 在不同屏幕尺寸上保持一致。

dependencies:
  flutter_screenutil: ^5.9.0
import 'package:flutter_screenutil/flutter_screenutil.dart';

// 在 main.dart 初始化
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      designSize: const Size(375, 812), // 设计稿尺寸
      minTextAdapt: true,
      builder: (context, child) {
        return MaterialApp(
          home: HomePage(),
        );
      },
    );
  }
}

// 使用
Container(
  width: 100.w,   // 宽度适配
  height: 50.h,   // 高度适配
  padding: EdgeInsets.all(16.r), // 圆角/间距适配
  child: Text(
    '你好',
    style: TextStyle(fontSize: 16.sp), // 字体适配
  ),
)

shimmer(骨架屏/闪光加载效果)

优雅的加载占位效果。

dependencies:
  shimmer: ^3.0.0
import 'package:shimmer/shimmer.dart';

// 基本用法
Shimmer.fromColors(
  baseColor: Colors.grey[300]!,
  highlightColor: Colors.grey[100]!,
  child: Container(
    width: 200,
    height: 100,
    color: Colors.white,
  ),
)

// 列表骨架屏
ListView.builder(
  itemCount: 5,
  itemBuilder: (context, index) {
    return Shimmer.fromColors(
      baseColor: Colors.grey[300]!,
      highlightColor: Colors.grey[100]!,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            // 头像占位
            Container(
              width: 50,
              height: 50,
              decoration: const BoxDecoration(
                color: Colors.white,
                shape: BoxShape.circle,
              ),
            ),
            const SizedBox(width: 16),
            // 文字占位
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Container(
                    width: double.infinity,
                    height: 16,
                    color: Colors.white,
                  ),
                  const SizedBox(height: 8),
                  Container(
                    width: 100,
                    height: 14,
                    color: Colors.white,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  },
)

flutter_spinkit(加载动画)

多种漂亮的加载动画。

dependencies:
  flutter_spinkit: ^5.2.0
import 'package:flutter_spinkit/flutter_spinkit.dart';

// 各种加载动画
SpinKitWave(color: Colors.blue, size: 50)
SpinKitCircle(color: Colors.blue, size: 50)
SpinKitCubeGrid(color: Colors.blue, size: 50)
SpinKitPulse(color: Colors.blue, size: 50)
SpinKitFadingCircle(color: Colors.blue, size: 50)
SpinKitDoubleBounce(color: Colors.blue, size: 50)
SpinKitThreeBounce(color: Colors.blue, size: 50)

pull_to_refresh(下拉刷新/上拉加载)

强大的下拉刷新和上拉加载组件。

dependencies:
  pull_to_refresh: ^2.0.0
import 'package:pull_to_refresh/pull_to_refresh.dart';

class MyListPage extends StatefulWidget {
  @override
  State<MyListPage> createState() => _MyListPageState();
}

class _MyListPageState extends State<MyListPage> {
  final RefreshController _refreshController = RefreshController();
  List<String> items = [];
  int page = 1;

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    // 加载数据...
    final newItems = await fetchItems(page);
    setState(() {
      items = newItems;
    });
  }

  Future<void> _onRefresh() async {
    page = 1;
    final newItems = await fetchItems(page);
    setState(() {
      items = newItems;
    });
    _refreshController.refreshCompleted();
  }

  Future<void> _onLoading() async {
    page++;
    final newItems = await fetchItems(page);
    if (newItems.isEmpty) {
      _refreshController.loadNoData();
    } else {
      setState(() {
        items.addAll(newItems);
      });
      _refreshController.loadComplete();
    }
  }

  @override
  Widget build(BuildContext context) {
    return SmartRefresher(
      controller: _refreshController,
      enablePullDown: true,
      enablePullUp: true,
      header: const WaterDropHeader(),
      footer: const ClassicFooter(
        loadingText: '加载中...',
        noDataText: '没有更多了',
        idleText: '上拉加载更多',
      ),
      onRefresh: _onRefresh,
      onLoading: _onLoading,
      child: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) => ListTile(
          title: Text(items[index]),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _refreshController.dispose();
    super.dispose();
  }
}

🎯 状态管理

provider(官方推荐)

Flutter 官方推荐的状态管理方案。

dependencies:
  provider: ^6.1.1
import 'package:provider/provider.dart';

// 1. 创建状态类
class CounterProvider extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// 2. 在顶层提供状态
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterProvider(),
      child: const MyApp(),
    ),
  );
}

// 3. 在 Widget 中使用
class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        // 读取状态
        child: Consumer<CounterProvider>(
          builder: (context, counter, child) {
            return Text('Count: ${counter.count}');
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 修改状态
          context.read<CounterProvider>().increment();
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

get(GetX)

功能最全面的包:状态管理 + 路由 + 依赖注入 + 国际化。

dependencies:
  get: ^4.6.6
import 'package:get/get.dart';

// 1. 创建 Controller
class CounterController extends GetxController {
  var count = 0.obs;  // .obs 让变量变成响应式

  void increment() => count++;
}

// 2. 使用 GetMaterialApp
void main() {
  runApp(GetMaterialApp(home: HomePage()));
}

// 3. 在页面中使用
class HomePage extends StatelessWidget {
  // 注入 Controller
  final controller = Get.put(CounterController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        // Obx 自动监听响应式变量
        child: Obx(() => Text('Count: ${controller.count}')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

// GetX 路由(无需 context)
Get.to(() => DetailPage());           // 跳转
Get.toNamed('/detail');               // 命名路由
Get.back();                           // 返回
Get.off(() => LoginPage());           // 跳转并移除当前页
Get.offAll(() => HomePage());         // 清空所有路由

// GetX SnackBar(无需 context)
Get.snackbar('标题', '内容');

// GetX Dialog
Get.defaultDialog(
  title: '提示',
  middleText: '确定要删除吗?',
  confirm: TextButton(
    onPressed: () => Get.back(),
    child: const Text('确定'),
  ),
);

riverpod(Provider 升级版)

更强大、更灵活的状态管理。

dependencies:
  flutter_riverpod: ^2.4.10
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 1. 创建 Provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
}

// 2. 使用 ProviderScope 包裹应用
void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

// 3. 在 Widget 中使用(继承 ConsumerWidget)
class CounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Scaffold(
      body: Center(
        child: Text('Count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

🧭 路由管理

go_router(官方推荐)

Flutter 官方推荐的声明式路由。

dependencies:
  go_router: ^13.2.0
import 'package:go_router/go_router.dart';

// 1. 定义路由
final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomePage(),
    ),
    GoRoute(
      path: '/detail/:id',
      builder: (context, state) {
        final id = state.pathParameters['id']!;
        return DetailPage(id: id);
      },
    ),
    GoRoute(
      path: '/settings',
      builder: (context, state) => const SettingsPage(),
    ),
  ],
  errorBuilder: (context, state) => const NotFoundPage(),
);

// 2. 使用 GoRouter
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: router,
    );
  }
}

// 3. 导航
context.go('/detail/123');          // 跳转
context.push('/detail/123');        // 入栈
context.pop();                      // 返回
context.goNamed('detail', pathParameters: {'id': '123'});

💾 本地存储

shared_preferences(轻量存储)

存储简单的键值对数据。

dependencies:
  shared_preferences: ^2.2.2
import 'package:shared_preferences/shared_preferences.dart';

// 保存数据
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', '长安');
await prefs.setInt('age', 25);
await prefs.setBool('isVip', true);
await prefs.setStringList('tags', ['Flutter', 'Dart']);

// 读取数据
final username = prefs.getString('username') ?? '';
final age = prefs.getInt('age') ?? 0;
final isVip = prefs.getBool('isVip') ?? false;
final tags = prefs.getStringList('tags') ?? [];

// 删除数据
await prefs.remove('username');

// 清空所有
await prefs.clear();

hive(高性能 NoSQL 数据库)

轻量级、高性能的本地数据库。

dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0

dev_dependencies:
  hive_generator: ^2.0.1
  build_runner: ^2.4.8
import 'package:hive_flutter/hive_flutter.dart';

// 1. 初始化
void main() async {
  await Hive.initFlutter();
  runApp(const MyApp());
}

// 2. 基本使用
final box = await Hive.openBox('settings');

// 存储
await box.put('theme', 'dark');
await box.put('fontSize', 16);

// 读取
final theme = box.get('theme', defaultValue: 'light');

// 删除
await box.delete('theme');

// 3. 存储对象(需要注册适配器)
@HiveType(typeId: 0)
class User extends HiveObject {
  @HiveField(0)
  late String name;

  @HiveField(1)
  late int age;
}

// 注册适配器(运行 build_runner 生成)
Hive.registerAdapter(UserAdapter());

// 存储对象
final userBox = await Hive.openBox<User>('users');
final user = User()
  ..name = '长安'
  ..age = 25;
await userBox.add(user);

sqflite(SQLite 数据库)

传统关系型数据库,适合复杂查询。

dependencies:
  sqflite: ^2.3.2
  path: ^1.8.3
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

// 数据库帮助类
class DatabaseHelper {
  static Database? _database;

  Future<Database> get database async {
    _database ??= await _initDatabase();
    return _database!;
  }

  Future<Database> _initDatabase() async {
    final path = join(await getDatabasesPath(), 'app.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE users(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT,
            email TEXT
          )
        ''');
      },
    );
  }

  // 插入
  Future<int> insertUser(Map<String, dynamic> user) async {
    final db = await database;
    return await db.insert('users', user);
  }

  // 查询
  Future<List<Map<String, dynamic>>> getUsers() async {
    final db = await database;
    return await db.query('users');
  }

  // 更新
  Future<int> updateUser(int id, Map<String, dynamic> user) async {
    final db = await database;
    return await db.update('users', user, where: 'id = ?', whereArgs: [id]);
  }

  // 删除
  Future<int> deleteUser(int id) async {
    final db = await database;
    return await db.delete('users', where: 'id = ?', whereArgs: [id]);
  }
}

🛠️ 工具类

intl(日期格式化/国际化)

日期时间格式化和国际化支持。

dependencies:
  intl: ^0.19.0
import 'package:intl/intl.dart';

final now = DateTime.now();

// 日期格式化
DateFormat('yyyy-MM-dd').format(now);        // 2024-01-15
DateFormat('yyyy年MM月dd日').format(now);     // 2024年01月15日
DateFormat('MM/dd/yyyy').format(now);        // 01/15/2024
DateFormat('HH:mm:ss').format(now);          // 14:30:25
DateFormat('yyyy-MM-dd HH:mm').format(now);  // 2024-01-15 14:30

// 本地化日期
DateFormat.yMMMMd('zh_CN').format(now);      // 2024年1月15日
DateFormat.EEEE('zh_CN').format(now);        // 星期一

// 数字格式化
NumberFormat('#,###').format(1234567);       // 1,234,567
NumberFormat.currency(locale: 'zh_CN', symbol: '¥').format(99.9);  // ¥99.90
NumberFormat.percentPattern().format(0.25);  // 25%

url_launcher(打开链接)

打开网页、拨打电话、发送邮件等。

dependencies:
  url_launcher: ^6.2.4
import 'package:url_launcher/url_launcher.dart';

// 打开网页
await launchUrl(Uri.parse('https://flutter.dev'));

// 在浏览器中打开
await launchUrl(
  Uri.parse('https://flutter.dev'),
  mode: LaunchMode.externalApplication,
);

// 拨打电话
await launchUrl(Uri.parse('tel:+8613800138000'));

// 发送短信
await launchUrl(Uri.parse('sms:+8613800138000'));

// 发送邮件
await launchUrl(Uri.parse('mailto:test@example.com?subject=Hello&body=Hi'));

// 打开地图
await launchUrl(Uri.parse('geo:39.9042,116.4074'));

// 检查是否可以打开
if (await canLaunchUrl(Uri.parse('https://flutter.dev'))) {
  await launchUrl(Uri.parse('https://flutter.dev'));
}

share_plus(分享功能)

调用系统分享功能。

dependencies:
  share_plus: ^7.2.2
import 'package:share_plus/share_plus.dart';

// 分享文本
await Share.share('查看这个链接:https://flutter.dev');

// 分享文本并指定主题
await Share.share(
  '这是一段分享内容',
  subject: '分享主题',
);

// 分享文件
await Share.shareXFiles(
  [XFile('/path/to/image.jpg')],
  text: '看看这张图片!',
);

// 分享多个文件
await Share.shareXFiles([
  XFile('/path/to/image1.jpg'),
  XFile('/path/to/image2.jpg'),
]);

permission_handler(权限管理)

统一的权限请求和检查。

dependencies:
  permission_handler: ^11.3.0
import 'package:permission_handler/permission_handler.dart';

// 检查权限
final status = await Permission.camera.status;
if (status.isGranted) {
  // 已授权
} else if (status.isDenied) {
  // 被拒绝,可以再次请求
} else if (status.isPermanentlyDenied) {
  // 永久拒绝,需要去设置页面开启
  await openAppSettings();
}

// 请求权限
final result = await Permission.camera.request();

// 请求多个权限
Map<Permission, PermissionStatus> statuses = await [
  Permission.camera,
  Permission.photos,
  Permission.location,
].request();

// 封装权限请求
Future<bool> requestCameraPermission() async {
  var status = await Permission.camera.status;
  if (status.isGranted) return true;

  status = await Permission.camera.request();
  if (status.isGranted) return true;

  if (status.isPermanentlyDenied) {
    // 提示用户去设置页面开启
    final shouldOpen = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('需要相机权限'),
        content: const Text('请在设置中开启相机权限'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(context, true),
            child: const Text('去设置'),
          ),
        ],
      ),
    );
    if (shouldOpen == true) {
      await openAppSettings();
    }
  }
  return false;
}

📋 推荐的 pubspec.yaml 模板

以下是一个实际项目常用的依赖配置:

name: my_app
description: A Flutter application.
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  
  # UI
  cupertino_icons: ^1.0.6
  flutter_screenutil: ^5.9.0
  shimmer: ^3.0.0
  flutter_spinkit: ^5.2.0
  cached_network_image: ^3.3.1
  
  # 网络
  dio: ^5.4.0
  
  # 状态管理(选择其一)
  provider: ^6.1.1
  # get: ^4.6.6
  
  # 路由
  go_router: ^13.2.0
  
  # 存储
  shared_preferences: ^2.2.2
  hive_flutter: ^1.1.0
  
  # 工具
  intl: ^0.19.0
  url_launcher: ^6.2.4
  share_plus: ^7.2.2
  permission_handler: ^11.3.0
  image_picker: ^1.0.7
  
  # 下拉刷新
  pull_to_refresh: ^2.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.1
  build_runner: ^2.4.8
  hive_generator: ^2.0.1

flutter:
  uses-material-design: true

📝 小结

这一章我们介绍了实际项目中最常用的第三方包:

分类推荐包用途
网络请求dioHTTP 客户端
图片处理cached_network_image, image_picker图片缓存、选择
UI 组件flutter_screenutil, shimmer屏幕适配、骨架屏
状态管理provider / get状态管理
路由go_router声明式路由
存储shared_preferences, hive本地存储
工具intl, url_launcher, permission_handler日期格式化、链接、权限

选包建议

  1. 优先用官方包 - 如 http、provider、go_router
  2. 看维护状态 - 最近更新时间超过 6 个月要谨慎
  3. 看 Pub Points - 140 分以上的包质量较好
  4. 看 Issues - 活跃的 Issues 说明有人在维护
  5. 不要过度依赖 - 能自己实现的简单功能就不要引入包

💪 练习题

  1. 使用 dio 封装一个 API 服务类,包含拦截器
  2. 使用 image_picker 实现选择图片并显示
  3. 使用 shared_preferences 实现简单的设置页面

🚀 下一步

了解了常用的第三方包后,你可以前往 附录A - UI 框架与组件库推荐,学习如何快速构建漂亮的界面!


由 编程指南 提供

最近更新: 2026/2/3 16:24
Contributors: 王长安
Prev
第16章 - 动画入门