Contents

Android 大作业报告

项目介绍

这次的大作业用的 Flutter 框架,虽然构建 UI 界面很容易,难的是各个组件之间的通信,以及对 async/await 的异步操作 这个应用由于时间原因选择了 Flutter 来开发,只实现了登录界面,笔记界面,通知界面 由于还没有对应用进行优化,解耦,数据库的操作只有应用开启时的读取数据,删除,添加,修改操作都没有实现

功能结构

登录界面

这里只提供了登录界面,没有提供登录的相关操作,不过表单还是有的

笔记界面

笔记界面的每一项由一个 NoteCard 组件构成,只提供了编辑功能,路由切换后会重置

需要编辑时点击右边的三点按钮,扩展的编辑面板显示

编辑完成后,点击 Save 保存之后

消息界面

消息界面完全仿照 原来的应用,没有什么稀奇的

设计类

这里介绍下这个项目的结构,重点不是代码,从入口讲起

入口类

void main() => runApp(App());

class App extends StatelessWidget {
  final title = 'HomeApp';
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      title: title,
      home: Home(),
    );
  }
}

主页的设计

主页中由三个页面,需要引入一个 TabView 组件来进行路由切换

其中主页为

class Home extends StatefulWidget {
  @override
  HomeState createState() => HomeState();
}

主页的构建由另一个类 HomeState 来实现,在其中使用 TabView

class HomeState extends State<Home> with SingleTickerProviderStateMixin{
  late final TabController controller = TabController(length: 3, vsync: this);

  @override
  void dispose() {
    // TODO: implement dispose
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
	bottom: TabBar(
	  controller: controller,
	  tabs: [
	    Tab(icon: Icon(Icons.login),),
	    Tab(icon: Icon(Icons.note),),
	    Tab(icon: Icon(Icons.message),),
	  ],
	),

	title: Text('Keep Alive'),
      ),

      body: TabBarView(
	controller: controller,
	children: [
	  LoginRoute(),
	  NoteRoute(),
	  NotificationRoute(),
	],
      ),
    );
  }

}

Login界面的设计

Login 的界面构建有些复杂,需要将其分解为几个部分

  1. buildTopBannerWidget

  2. buildAccountLoginTip

  3. buildEditWidget

  4. buildLoginButton

    之后再将这些组件整合到一个 Column 中即可 需要注意的是,对文本框进行编辑时,会出现底部空间不够绘制的情况,需要在 build 方法中使用 SingleChildScrollView 来处理

    @override
    Widget build(BuildContext context) {
      return SingleChildScrollView(
        child: buildColumn(context),
      );
    }
    

笔记界面的设计

在应用打开的时候,需要从数据库读取,再生成数据对象 NoteCard

class NoteCard extends StatefulWidget {
  String title;
  String content;

  NoteCard({required this.title, required this.content});

  @override
  CardState createState() => CardState(title: title, content: content);
}

通知界面也是如此 有关数据库的操作写在下面

数据库

由于 dart 是单线程的语言,打开数据库需要一定的时间,为了不影响整个程序的效率,dart 使用了异步的概念 这里将数据库的增,删,改,查操作都写在数据模型类中

数据模型的操作

存储笔记的数据模型 note 中

static Future<void> insert(Database db, Note note) async {
  await db.insert(
    tableName,
    note.toMap(),
    conflictAlgorithm: ConflictAlgorithm.replace
  );
}

static Future<void> delete(Database db, int id) async {
  await db.delete(tableName, where: 'id = ?', whereArgs: [id]);
}

static Future<void> update(Database db, Note note) async {
  await db.update(
    tableName, note.toMap(), where: 'id = ?', whereArgs: [note.id]);
}

static Future<List<Note>> all(Database db) async {
  final List<Map<String, dynamic>> maps = await db.query(tableName);
  return List.generate(
    maps.length,
    (index) =>
    Note(
      id: maps[index]['id'],
      title: maps[index]['title'],
      content: maps[index]['content']
  ));
}

类似的,存储消息的数据模型 notify 中

static Future<void> insert(Database db, Notify notification) async {
  await db.insert(
    tableName,
    notification.toMap(),
    conflictAlgorithm: ConflictAlgorithm.replace
  );
}

static Future<void> delete(Database db, int id) async {
  await db.delete(tableName, where: 'id = ?', whereArgs: [id]);
}

static Future<void> update(Database db, Notify notification) async {
  await db.update(
    tableName, notification.toMap(), where: 'id = ?', whereArgs: [notification.id]);
}

static Future<List<Notify>> all(Database db) async {
  final List<Map<String, dynamic>> maps = await db.query(tableName);
  return List.generate(
    maps.length,
    (index) =>
    Notify(
      id: maps[index]['id'],
      className: maps[index]['className'],
      notifyContent: maps[index]['notifyContent']
  ));
}

组件的构建

这里为什么要单独讲组件的构建,因为这里牵扯到数据的异步加载,还有 build 方法不能定义为异步的,具体参照这篇文章 另外这里应用的构建代码定义在路由界面中 在 NoteRoute 中,先定义一个异步回调

Future<List<NoteCard>> callAsyncFetch() async {
  final database = openDatabase(
    join(await getDatabasesPath(), 'todolist.db')
  );

  var cards = await Note.all(await database);
  return cards.map((e) => NoteCard(title: e.title, content: e.content)).toList();
}

再将 build 的返回值改为 FutureBuilder ,指定 future 与 builder

@override
Widget build(BuildContext context) {
  // TODO: implement build
  return FutureBuilder(
    future: callAsyncFetch(),
    builder: (context, AsyncSnapshot<List<NoteCard>> snapshot) {
      if(snapshot.hasData) {
	return ListView(
	  children: snapshot.data!,
	);
      } else {
	return CircularProgressIndicator();
      }
    }
  );
}