一个完整的Flutter项目的基本构成

目录

  • 1.页面跳转
  • 2.本地数据库和读取
      • [2.1 在pubspec.yaml中添加数据库框架依赖](#2.1 在pubspec.yaml中添加数据库框架依赖)
      • [2.2 创建db.dart 初始化数据库并创建表](#2.2 创建db.dart 初始化数据库并创建表)
      • [2.3 安装JsonToDart插件](#2.3 安装JsonToDart插件)
      • [2.4 创建实体类 user_bean.dart](#2.4 创建实体类 user_bean.dart)
      • [2.5 增删改查:](#2.5 增删改查:)
  • 3.网络请求+数据解析+UI渲染

本篇主要总结下一个完整的Flutter项目有哪些基本构成?
一般来说数据需要展示到页面上面大概需要:

网络请求+数据解析+UI渲染、
本地数据库、
页面跳转导航等

下面一点点开始构建

1.页面跳转

创建my_routers.dart 定义Router

c 复制代码
class MyRouter{
  static const String DOWNLOAD_PAGE = "/DownLoadPage";
  static const String LANG_PAGE = "/LangPage";

}

main.dart 中配置routes

c 复制代码
class MyApp extends StatelessWidget {
  final Locale locale;

  const MyApp(this.locale, {super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      routes: {
        MyRouter.DOWNLOAD_PAGE: (context) => DownLoadPage(),
        MyRouter.LANG_PAGE: (context) => LangPage(),
      },
    );
  }
}

执行跳转

c 复制代码
  Navigator.pushNamed(context, MyRouter.DOWNLOAD_PAGE);

2.本地数据库和读取

2.1 在pubspec.yaml中添加数据库框架依赖

c 复制代码
  sqflite: ^2.3.0

(简单数据保存可用 shared_preferences: ^2.2.2

2.2 创建db.dart 初始化数据库并创建表

c 复制代码
import 'package:sqflite/sqflite.dart';

//在Flutter中,创建表时可以使用以下数据类型:
//
// INTEGER:整数类型,可以存储整数值。
// REAL:浮点数类型,可以存储浮点数值。
// TEXT:文本类型,可以存储字符串值。
// BLOB:二进制类型,可以存储任意二进制数据。
// 此外,还可以使用以下修饰符来定义表中的列:
//
// PRIMARY KEY:主键修饰符,用于指定列作为主键。
// AUTOINCREMENT:自增修饰符,用于指定主键列自动增加。
// NOT NULL:非空修饰符,用于指定列的值不能为空。
// UNIQUE:唯一修饰符,用于指定列的值不能重复。

class DatabaseHelper {
  static Database? _database;
  //数据库名称
  static const String _dbName = 'demo.db';
  //数据库版本,如果表结构修改,需要增加
  static const int _dbVersion = 2;

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

  Future<Database> _initDatabase() async {
    return await openDatabase(_dbName, version: _dbVersion,
        onCreate: (db, version) async {
      String studentSQL = '''
     CREATE TABLE Students (
     id INTEGER PRIMARY KEY AUTOINCREMENT,
     name TEXT NOT NULL,
     age INTEGER,
     gpa REAL
   )
      ''';
      String userSQL = '''
     CREATE TABLE User (
     id INTEGER PRIMARY KEY,
     name TEXT NOT NULL,
     age INTEGER
   )
      ''';
      await db.execute(userSQL);
      await db.execute(studentSQL);
    }, onUpgrade: (db, oldVersion, newVersion) async {
      if (oldVersion == 1 && newVersion == 2) {
        //修改表结构
        await db.execute('ALTER TABLE User ADD sex TEXT');
      }
    });
  }

  //获取所有表
  Future<List<Map<String, dynamic>>> getAllTables() async {
    final Database db = await database;
    return db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'");
  }

  // 测试
  test() async {
    DatabaseHelper databaseHelper = DatabaseHelper();
    List<Map<String, dynamic>> tables = await databaseHelper.getAllTables();
    tables.forEach((table) {
      print("当前数据中的表:${table['name']}");
    });
  }
}

表创建好了,该增删改查了。

2.3 安装JsonToDart插件

我们先安装一个解析json数据插件:JsonToDart

Android Studio - File - Settings - Plugins 搜索JsonToDart安装重启Android Studio即可

安装好以后创建一个Bean文件:user_bean.dart 来接收数据

2.4 创建实体类 user_bean.dart

在lib下创建bean文件夹

对着lib/bean文件夹右键 选择 new - JsonToDart 输入json数据即可生成如下文件

c 复制代码
import 'dart:convert';

UserBean userBeanFromJson(String str) => UserBean.fromJson(json.decode(str));

String userBeanToJson(UserBean data) => json.encode(data.toJson());

class UserBean {
  UserBean({
    this.id,
    this.name,
    this.age,
  });

  UserBean.fromJson(dynamic json) {
    id = json['id'];
    name = json['name'];
    age = json['age'];
  }

  num? id;
  String? name;
  num? age;

  UserBean copyWith({
    num? id,
    String? name,
    num? age,
  }) =>
      UserBean(
        id: id ?? this.id,
        name: name ?? this.name,
        age: age ?? this.age,
      );

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['id'] = id;
    map['name'] = name;
    map['age'] = age;
    return map;
  }
}

2.5 增删改查:

c 复制代码
//增加
     var db = await DatabaseHelper().database;
    int id = await db.insert('User', userBean.toJson(),   
      //插入冲突策略(如果同样的对象被插入两次,则后者替换前者)
      conflictAlgorithm: ConflictAlgorithm.replace);
    Log.i("添加成功,id = : $id");

//删除
    //where中的第一个?对应whereArgs数组的第一个
    var re = await db.delete('Wallet', where: 'id = ?', whereArgs: [1]);
    Log.i("删除成功 ===  $re");

//修改
   var db = await DatabaseHelper().database;
    List list = await db.query("User");
    var wList = list.map((e) => UserBean.fromJson(e)).toList();
    var w = wList[0];
    w.name = "feifei";
    var re = await db.update('User', w.toJson(),
        where: 'id = ?', whereArgs: [w.id]);

    Log.i("修改成功 ===  $re");

//查询
   List list = await db.query("User");
   var wList = list.map((e) => UserBean.fromJson(e)).toList();
   var w = wList[0];

3.网络请求+数据解析+UI渲染

pubspec.yaml中引入依赖:

c 复制代码
  dio: ^5.3.3

创建http.dart 简单封装dio

c 复制代码
import 'package:dio/dio.dart';
import 'app_urls.dart';

class Http {
  static Dio? _dio;

  static Http of({String? baseUrl}) {
    return Http._initDio(baseUrl: baseUrl);
  }

  Http._initDio({String? baseUrl}) {
    if (_dio == null) {
      _dio = Dio();
      Iterable<Interceptor> iterable = [
        LogInterceptor(requestBody: true, responseBody: true),
      ];
      _dio?.interceptors.add(InterceptorsWrapper(
        onRequest: (options, handler) {
          // 在请求被发送之前做一些事情
          // 设置公共header
          options.headers.addAll({'au_header': '1'});
          // 设置公共参数
          //options.queryParameters.addAll({'token': 'your_token'});
          return handler.next(options); // 必须调用 next 方法
        },
        onResponse: (response, handler) {
          // 在响应被处理之前做一些事情
          return handler.next(response); // 必须调用 next 方法
        },
        onError: (DioError e, handler) {
          // 在请求发生错误时做一些事情
          return handler.next(e); // 必须调用 next 方法
        },
      ));
      _dio?.interceptors.addAll(iterable);
    }
    var options = BaseOptions(
      baseUrl: baseUrl ?? AppUrls.BASE_URL,
      connectTimeout: const Duration(seconds: 5),
      sendTimeout: const Duration(seconds: 5),
      receiveTimeout: const Duration(seconds: 5),
    );

    _dio?.options = options;
  }

  //fzm-platform-id

  Future<HttpResponse<dynamic>> get(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async {
    var response = await _dio!.get(path,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress);
    return parse(response);
  }

  Future<HttpResponse<dynamic>> post(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async {
    var response = await _dio!.post(path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress);

    return parse(response);
  }
}

HttpResponse parse(Response response) {
  //真正的解析
  var code = response.data["code"];
  var data = response.data["data"];
  var result = response.data["result"];
  var error = response.data["error"];
  if (code == 0 || code == null) {
    //赋值给构造函数
    return HttpResponse.success(data ?? result);
  } else {
    return HttpResponse.failure(error ?? "${code}");
  }
}

// 注册返回:{data: null, code: -1}
class HttpResponse<T> {
  bool ok = false;
  T? data;
  String? error;

  //this.data是赋值简写
  HttpResponse.success(this.data) {
    ok = true;
  }
  //完整写法
  // HttpResponse.success(T? data) {
  //     this.data = data;
  //     ok = true;
  //   }

  HttpResponse.failure(this.error) {
    ok = false;
  }
}

使用dio 请求并解析渲染到UI

c 复制代码
  final List<UserBean> _userList = [];
  Future<void> getExs() async {
    var response = await Http.of().post("https://");
    if (response.ok) {
      List list = response.data;
      //解析数据
      List<UserBean> userList = list.map((e) => UserBean.fromJson(e)).toList();
      setState(() {
        _userList.addAll(userList);
      });
    }
  }

渲染到ListView中

c 复制代码
ListView.builder(
        itemCount: _userList.length,
        itemBuilder: (context, index) =>
            InkWell(
                onTap: (){
                  Navigator.pushNamed(context, MyRouter.WEB_PAGE,arguments: {
                    "name":_userList[index].name
                  });
                  toast(_userList[index].name??"");
                },
                child: Container(child: UserItem(_userList[index]))));
c 复制代码
class UserItem extends StatelessWidget {
  final UserBean userBean;

  const UserItem(this.userBean, {super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(left: 20, right: 20, top: 20),
      child: Row(
        children: [Text(userBean.name ?? "")],
      ),
    );
  }
}

完。

相关推荐
科昂15 分钟前
Dart 单线程异步模型:从原理到工程实践的系统化解析
android·flutter·dart
fundroid1 小时前
2025 跨平台技术如何选:KMP 与 Flutter 的核心差异
flutter·kotlin·kmp
耳東陈3 小时前
Flutter开箱即用一站式解决方案-新增企业级日志
flutter
顾林海3 小时前
Flutter 图片组件全面解析:从基础加载到高级应用
android·前端·flutter
眼镜会飞3 小时前
Flutter window和Mac中webview2使用Cef替代
windows·flutter·mac
淡写成灰4 小时前
Flutter自定义带有Badger组件组
flutter
好的佩奇4 小时前
Dart 之任务
android·flutter·dart
豪冷啊18 小时前
Flutter Invalid constant value.
flutter
顾林海21 小时前
Flutter容器组件深度解析
android·前端·flutter
xq952721 小时前
mac os flutter 配置环境变量
flutter