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

本文概述了一个完整的Flutter项目构建过程,包括页面跳转、使用SQLite数据库进行增删改查操作,以及使用Dio库进行网络请求和数据解析,最后展示了如何将数据渲染到UI界面。
摘要由CSDN通过智能技术生成


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

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

下面一点点开始构建

1.页面跳转

创建my_routers.dart 定义Router

class MyRouter{
  static const String DOWNLOAD_PAGE = "/DownLoadPage";
  static const String LANG_PAGE = "/LangPage";

}

main.dart 中配置routes

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(),
      },
    );
  }
}

执行跳转

  Navigator.pushNamed(context, MyRouter.DOWNLOAD_PAGE);

2.本地数据库和读取

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

  sqflite: ^2.3.0

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

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

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数据即可生成如下文件

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 增删改查:

//增加
     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中引入依赖:

  dio: ^5.3.3

创建http.dart 简单封装dio

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;
  }


  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

  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中

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]))));
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 ?? "")],
      ),
    );
  }
}

完。

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哆啦A梦z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值