一个完整的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 ?? "")],
      ),
    );
  }
}

完。

相关推荐
奋斗的小青年!!2 小时前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘5 小时前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!8 小时前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨9 小时前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者9610 小时前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨12 小时前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei12 小时前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei12 小时前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!12 小时前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_12 小时前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter