1.安装开发环境
1.到官网下载Flutter的SDK包下载地址

2.将包解压到自己定义的目录

3.在环境变量里添加包的地址

4.在终端测试是否安装成功
注意:验证安装是否成功时,会自动下载相关依赖内容,但要配置镜像,否则很慢
// 配置镜像
set PUB_HOSTED_URL=https://pub.flutter-io.cn
set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
// 检查安装
flutter doctor

5.下载开发工具
我这里用的是vs code,安装插件:
- Dart:支持Dart语言
- Flutter:支持Flutter语言
- Flutter Widget Snippets:提供快捷键方法
2.创建项目
1.创建项目,具体还有一些其他命令,可参考官网
flutter create lian_flutter

2.初始化项目结构
-
在lib目录里创建相关目录
api:接口
assets:静态资源
components:公共组件
contants:常量文件
page:页面
routes:路由
stores:全局状态
utils:工具类

3.启动项目
-
在main.dart目录里启动项目

-
使用命令符启动
flutter run : 默认启动,会让指定启动设备
flutter run -d edge:指定IE浏览器启动
3.封装路由
1.在main.dart里包裹路由部件
Future<void> main(List<String> args) async {
runApp(getRootwidget());
}
2.封装路由部件,将编写的部件引入
Widget getRootwidget() {
final RouteGuard _routeGuard = RouteGuard();
return MaterialApp(
initialRoute: "/", // 默认主页
navigatorObservers: [_routeGuard],// 全局路由
routes: getRootRoutes(),
);
}
Map<String, Widget Function(BuildContext)> getRootRoutes() {
return {
"/": (contex) => MainPage(),
"/login": (contex) => LoginPage(),
"/detail":(contex)=> DetailPage()
};
}
3.封装路由守卫,根据需求修改内容
import 'package:flutter/material.dart';
class RouteGuard extends NavigatorObserver {
// ========== 类似 Vue 的 beforeEach ==========
@override
void didPush(Route route, Route? previousRoute) {
super.didPush(route, previousRoute);
_handleRouteChange(route, 'push');
}
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
print('离开: ${route.settings.name}');
}
@override
void didRemove(Route route, Route? previousRoute) {
super.didRemove(route, previousRoute);
print('移除: ${route.settings.name}');
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
print('替换: ${oldRoute?.settings.name} -> ${newRoute?.settings.name}');
}
// ========== 路由变化处理 ==========
void _handleRouteChange(Route route, String action) {
final routeName = route.settings.name ?? 'unknown';
print('[$action] 进入路由: $routeName');
// 权限验证
_checkAuth(route);
}
// ========== 权限验证 ==========
void _checkAuth(Route route) {
final routeName = route.settings.name;
// 需要登录才能访问的路由列表
final authRoutes = ['/cart',"/my"];
if (authRoutes.contains(routeName)) {
// 检查是否登录(假设用共享偏好或状态管理)
final isLoggedIn = false; // 从状态管理获取
if (!isLoggedIn) {
print('未登录,跳转到登录页');
// 这里不能直接跳转,需要通过 Navigator 的上下文
// 实际使用中需要获取 context
}
}
}
}
4.封装Dio请求
-
Dio依赖是Flutter提供的http请求
-
安装依赖
flutter pub add dio
-
封装请求
// 需要在main.dart里进行初始化缓存和Http
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:lian_flutter/page/Login/index.dart';
import 'package:lian_flutter/stores/TokenManager.dart';
import 'package:shared_preferences/shared_preferences.dart';class Http {
static final Dio _dio = Dio();// 统一错误处理 static String _handleError(DioException error) { switch (error.type) { case DioExceptionType.connectionTimeout: return '网络连接超时,请检查网络'; case DioExceptionType.receiveTimeout: return '服务器响应超时,请稍后重试'; case DioExceptionType.sendTimeout: return '数据发送超时,请检查网络'; case DioExceptionType.connectionError: return '网络连接失败,请检查网络设置'; case DioExceptionType.cancel: return '请求已取消'; case DioExceptionType.badResponse: final statusCode = error.response?.statusCode; switch (statusCode) { case 400: return '请求参数错误'; case 401: return '未授权,请重新登录'; case 403: return '没有权限访问'; case 404: return '接口不存在'; case 500: return '服务器内部错误'; default: return '服务器错误 ($statusCode)'; } default: return error.message ?? '网络请求失败'; } } // 初始化配置 static void init() { _dio.options.baseUrl = 'https://meikou-api.itheima.net'; // 配置基地址 _dio.options.connectTimeout = const Duration(seconds: 10); // 设置请求超时时间 _dio.options.receiveTimeout = const Duration(seconds: 10); // 设置响应超时时间 /** * 不设置 contentType,Dio 默认会根据 data 自动判断 * application/json JSON 格式 {"name":"张三","age":18} ✅ 最常用,RESTful API * application/x-www-form-urlencoded 表单格式 name=张三&age=18 传统表单提交 * multipart/form-data 文件上传格式 二进制数据 上传图片/文件 */ // _dio.options.contentType = "application/x-www-form-urlencoded"; // 拦截器 _dio.interceptors.add( InterceptorsWrapper( // 请求拦截 onRequest: (request, handler) { // 获取token final token = tokenManager.getToken(); if (token.isNotEmpty) { request.headers['Authorization'] = 'Bearer $token'; } print('请求: ${request.method} ${request.path}'); // 下一步 handler.next(request); }, // 响应拦截 onResponse: (response, handler) { print('响应: ${response.statusCode}'); final data = response.data; if (data is Map && data.containsKey('code')) { if (data['code'] == "1") { // 具体看后端如何定义成功code和数据类型 response.data = data['result']; } else { // 业务错误抛出异常 throw DioException( error: data['msg'] ?? '业务错误', requestOptions: response.requestOptions, ); } } handler.next(response); }, // 错误拦截 onError: (error, handler) { // 获取错误内容 final errorMsg = _handleError(error); print('错误: ${error.message}'); // 特殊处理,401自动跳转登录 if (error.response?.statusCode == 401) { // 清除token,跳转登录页 tokenManager.removeToken(); // 这里用你的路由跳转方式 Get.to(() => LoginPage()); } // 构造新错误 final handledError = DioException( error: errorMsg, requestOptions: error.requestOptions, response: error.response, type: error.type, ); handler.next(handledError); }, ), ); } // 封装get方法 static Future get(String url, {Map<String, dynamic>? params}) async { try { final response = await _dio.get(url, queryParameters: params); return response.data; } on DioException { // 直接抛出,因为已经在 onError 中处理过了 rethrow; } catch (e) { // 其他未知错误 throw '未知错误: $e'; } } // 封装post方法 static Future post(String url, {Map<String, dynamic>? data}) async { try { final response = await _dio.post(url, data: data); return response.data; } on DioException { rethrow; } catch (e) { throw '未知错误: $e'; } }}
-
在main.dart里初始化
Future
main(List args) async {
// 初始化Flutter引擎,否则下面内容报错
WidgetsFlutterBinding.ensureInitialized();// 初始化Dio Http.init(); runApp(getRootwidget());}
-
定义接口方法
import 'package:lian_flutter/api/Http.dart';
import 'package:lian_flutter/viewmodels/Category.dart';
import 'package:lian_flutter/viewmodels/user.dart';// 调用方法不同
// Api.getCarouselAPI()调用,是静态方法
class Api {
// 获取轮播图
static Future<List> getCarouselAPI() async {
final result = await Http.get('/home/banner');// ✅ 将 List<dynamic> 转换为 List<BannerItem> if (result is List) { return BannerItem.fromList(result); } return []; }}
// 登录,可以直接引入,然后调用loginAPI()
FutureloginAPI(Map<String, dynamic> data) async {
return UserInfo.fromJSON(await Http.post('/login', data: data));
}// 获取用户信息
FuturegetUserInfoAPI() async {
return UserInfo.fromJSON(await Http.get("/member/profile"));
} -
使用接口
调用class里的接口
void _getBannderList() async {
_bannerItem = await Api.getCarouselAPI();
setState(() {});
}调用方法api
_initUser() async {
await tokenManager.init();
var token = tokenManager.getToken();
if (token.isNotEmpty) {
final res = await getUserInfoAPI();
// 登录成功后更新共享数据
_userController.updateUserInfo(res);
}
}
5.封装全局属性GetX和缓存shared_preferences
1.安装依赖
// 安装GetX
flutter pub add get
// 安装缓存
flutter pub add shared_preferences
2.Getx封装
-
封装通用数据方法类UserController
class UserController extends GetxController {
var user = UserInfo.fromJSON({}).obs;// user对象监听,要给初始值结尾加.obs
// 获取值要到user.value里取值// 更新数据对象 updateUserInfo(UserInfo newUser){ user.value = newUser; } // 清空数据 logout(){ user.value = UserInfo.fromJSON({}); }}
-
在main.dart里初始化GetX
Future
main(List args) async {
// 初始化Flutter引擎,否则下面内容报错
WidgetsFlutterBinding.ensureInitialized();// 初始化GetX Get.put(UserController()); runApp(getRootwidget());}
-
存放全局数据
// 1.定义find实列 final UserController _userController = Get.find(); // 2.调用定义的更新方法,将最新数据存入 _userController.updateUserInfo(res); -
使用全局数据
// 1.定义find实列 final UserController _userController = Get.find(); // 获取共享数据内容 _userController.user.value.id
3.封装缓存数据shared_preferences
-
封装缓存数据类TokenManager
class TokenManager {
late SharedPreferences _prefs;
String _token = '';// 初始化token Future<void> init() async { // 1.获取持久化实例,里面有一系列方法 _prefs = await SharedPreferences.getInstance(); // 2.设置token _token = _prefs.getString("set_token") ?? ''; } // 设置token Future<void> setToken(String val) async { _prefs.setString("set_token", val); _token = val; } // 获取token,要用同步方法,否则会报异常,所有在初始化里调用并赋值 String getToken() { return _token; } // 删除token Future<void> removeToken() async { _prefs.remove("set_token");// 删除磁盘缓存 _token = '';// 删除内存缓存 }}
final tokenManager = TokenManager();// 实例化
-
在main.dart里初始化缓存
Future
main(List args) async {
// 初始化Flutter引擎,否则下面内容报错
WidgetsFlutterBinding.ensureInitialized();// 初始化持久化 await tokenManager.init(); runApp(getRootwidget());}
-
设置缓存数据,调用缓存方法
tokenManager.setToken(res.token);
-
使用缓存数据
tokenManager.getToken()