Flutter学习之事件循环机制、数据库、网络请求

// 具体逻辑
});

上面就是将一个任务加到MicroTask队列中去。

1.7.例子1

import ‘dart:async’;
main() {
print(‘main #1 of 2’);
scheduleMicrotask(() => print(‘microtask #1 of 2’));

new Future.delayed(new Duration(seconds:1),
() => print(‘future #1 (delayed)’));
new Future(() => print(‘future #2 of 3’));
new Future(() => print(‘future #3 of 3’));

scheduleMicrotask(() => print(‘microtask #2 of 2’));

print(‘main #2 of 2’);
}

输出结果:

main #1 of 2
main #2 of 2
microtask #1 of 2
microtask #2 of 2
future #2 of 3
future #3 of 3
future #1 (delayed)

上面执行顺序:main方法 ->Microtask队列->Event队列(先 new Future 后new Future.delay),下面直接拿官方的例子实践一下:

1.8.例子2

import ‘dart:async’;
main() {
print(‘main #1 of 2’);
scheduleMicrotask(() => print(‘microtask #1 of 3’));

new Future.delayed(new Duration(seconds:1),
() => print(‘future #1 (delayed)’));

new Future(() => print(‘future #2 of 4’))
.then(() => print(‘future #2a’))
.then((
) {
print(‘future #2b’);
scheduleMicrotask(() => print(‘microtask #0 (from future #2b)’));
})
.then((_) => print(‘future #2c’));

scheduleMicrotask(() => print(‘microtask #2 of 3’));

new Future(() => print(‘future #3 of 4’))
.then(() => new Future(
() => print(‘future #3a (a new future)’)))
.then((
) => print(‘future #3b’));

new Future(() => print(‘future #4 of 4’));
scheduleMicrotask(() => print(‘microtask #3 of 3’));
print(‘main #2 of 2’);
}

输出结果:

main #1 of 2
main #2 of 2
microtask #1 of 3
microtask #2 of 3
microtask #3 of 3
future #2 of 4
future #2a
future #2b
future #2c
microtask #0 (from future #2b)
future #3 of 4
future #4 of 4
future #3a (a new future)
future #3b
future #1 (delayed)

上面两个小例子会加深对事件消息的理解。

三、Dart中的异步支持

因为Dart是单线程语言,当遇到延迟的运算(I/O操作),线程中顺序执行的运算就会阻塞,那就app上,用户操作就会感到卡顿,于是通常用异步处理来解决这个问题,当遇到需要延迟的运算时,就会放入延迟运算的队列中,先把不需要延迟的运算先执行,最后再来处理延迟运算。Dart类库有非常多的返回Future或者Stream对象的函数,这些函数被称为异步函数;它们会在设置好一些需要消耗一定时间的操作之后返回,比如I/O操作,而不是等到这个操作完成。

1.Future

什么是Future,顾名思义,表示一件将来会发生的事情(也就是不会立即执行),将来可以从Future中取到一个值,当一个方法返回一个Future的事情,发生两件事:

  1. 这个方法将某件事情排队,返回一个未完成的Future
  2. 这个方法事情完毕后,Future的状态会变成已经完成,这个时候可以取到这件事情的返回值。

Future表示一个异步操作的最终完成(或失败)及其结果值的表示,简单来说,它就是用来处理异步操作的,异步处理成功就执行成功的操作,异步处理失败就捕获错误或者停止后续操作,一个Future只会对应一个结果,要么成功,要么失败。

1.1.Future.then

main() {
create();
}

//模执行延时任务
void create(){
//延迟三秒执行
Future.delayed(new Duration(seconds: 3),(){
return “This is data”;
}).then((data){
print(data);
});
}

输出结果如下:

This is data

上面可以发现,使用Future.delayed创建一个延时任务,当三秒后通过thendata接收了这个所返回的This is data这个字符串的值。下面读取一个文件,先创建一个文件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面读取内容看看:

main() {
create();
}
void create(){
//延迟三秒执行
var file = File(“/Users/luguian/Downloads/flutter第五天/flutter.rtf”);
//定义了返回结果值为String类型
Future data = file.readAsString();
//返回文件内容
data.then((text){
//打印文件内容
print(text);
});
print(“I love Android”);
}

I love Android -->先打印I love Android
{\rtf1\ansi\ansicpg936\cocoartf1561\cocoasubrtf600
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset134 PingFangSC-Regular;}
{\colortbl;\red255\green255\blue255;}
{*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx566\tx1133\tx1700\tx2083\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0

\f0\fs24 \cf0 I love flutter —>文字内容
\f1 ;}

可以发现,先输出I love Android然后读取文件这种超时的操作会后执行,也就是读取文件内容是将来执行的then接收异步并打印出结果。注意:Future并不是并行执行的

1.2.Future.catchError

当异步任务发生错误,可以在catchError中捕获错误,例子如下:

Future.delayed(new Duration(seconds: 3),(){
throw AssertionError(“This is a Error”);
}).then((data){
//这是成功的逻辑
print(“success”);
}).catchError((e){
//失败会走到这里
print(e);
});

输出结果如下:

Assertion failed

可以发现,在异步任务中抛出了一个异常,then的回调函数不会执行,反而catchError函数被调用,当然并不是只有catchError才能捕获错误,then方法有一个可选的参数onError,可以用它来捕获异常:

Future.delayed(new Duration(seconds: 3),(){
throw AssertionError(“This is a Error”);
}).then((data){
//这是成功的逻辑
print(“success”);
},onError:(e){
print(e);
});

输出结果:

Assertion failed

1.3.Future.whenComplete

有很多时候,当异步任务无论成功或者失败都需要做一些事的场景,如在网络请求前弹出加载进度框,在请求结束后关闭进度框,下面用whenComplete进行回调,例子如下:

Future.delayed(new Duration(seconds: 3),(){
throw AssertionError(“This is a Error”);
}).then((data){
//这是成功的逻辑
print(“success”);
},onError:(e){
//这是失败的逻辑
print(e);
}).whenComplete((){
print(“无论失败,或者成功都会走到这”);
});

}

输出结果如下:

Assertion failed
无论失败,或者成功都会走到这

1.4.Future.wait

有时候,需要等待多个异步任务都执行结束后才进行一些操作,如有一个界面,需要从两个接口获取数据,获取成功后,将两个数据进行处理后显示在UI界面上,这时候,Future.wait派出用上了,它接收一个Future数组参数,只有数组中所有的Future执行成功后,就会触发then回调,当然,只要有一个Future执行失败就会触发错误回调,下面实现一下当两个异步任务都执行成功时,将结果打印出来:

Future.wait([
//3秒后返回结果
Future.delayed(new Duration(seconds: 3),(){
return “Android”;
}),
//4秒后返回结果
Future.delayed(new Duration(seconds: 4),(){
return " And Future";
})
]).then((data){
//成功逻辑
print(data[0] + data[1]);
}).catchError((e){
//捕捉错误
print(e);
});
}

输出结果如下:

Android And Future

可以看到当两个异步任务完成才会回调then函数。

2.Async/await

使用Async/await也是可以实现异步操作,下面直接上例子:

main() {
create();
}
void create(){
String data = getData();
print(data);
print(“I love Future”);
}
getData() async{
return await “I love Android”;
}

运行上面代码,报错了:

type ‘Future’ is not a subtype of type ‘String’

报的是类型不匹配?为什么呢?经过一番搜查,发现getData是一个异步操作函数,它的返回值是一个await延迟执行的结果。在Dart中,有await标记的运算,其结果值是一个Future对象,Future并不是String类型,就报错了。那么怎么才正确获得异步的结果呢?Dart规定async标记的函数,只能由await来调用,下面改成这样:

main() {
create();
}

void create() async{
String data = await getData();
print(data);
print(“I love Future”);

}
getData() async{
return await “I love Android”;
}

下面直接去掉async函数包装,直接在getData方法里对data进行赋值:

String data;

main() {
create();
}

void create(){
getData();
print(“I love Future”);

}
getData() async{
data = await “I love Android”;
print(data);
}

上面输出结果是:

I love Future
I love Android

可以发现,先输出的是I love Future后面再输出I love Android,可以发现当函数被async修饰时,会先去执行下面的操作,当下面的操作执行完,然后再执行被async修饰的方法。async用来表示函数是异步的,定义的函数会返回一个Future对象,await后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走。要注意以下几点:

  1. await关键字必须在async函数内部使用,也就是加await不加async会报错。
  2. 调用async函数必须使用await关键字,如果加async不加await会顺序执行代码。

下面再上例子:

main() {
_startMethod();
_method_C();

}

_startMethod() async{
_method_A();
await _method_B();
print(“start结束”);
}
_method_A(){
print(“A开始执行这个方法~”);

}

_method_B() async {
print(“B开始执行这个方法~”);
await print(“后面执行这句话~”);
print(“继续执行这句哈11111~”);
}

_method_C(){
print(“C开始”);
}

结果如下:

A开始执行这个方法~
B开始执行这个方法~
后面执行这句话~
C开始
继续执行这句哈11111~
start结束

  1. 当使用async作为方法名后缀声明时,说明这个方法的返回值是一个Future
  2. 当执行到该方法代码用await关键字标注时,会暂停该方法其他部分执行;
  3. await关键字引用的Future执行完成,下一行代码会立即执行。

也就是首先执行_startMethod这个方法用async声明了,因为方法里调用了_method_A,所以先输出print(“A开始执行这个方法~”);,后面执行_method_B(),这个方法用await关键字声明,所以会暂停print(“start结束”);的执行,然后继续执行_method_B() print(“B开始执行这个方法");**输出,下一行遇到**await**关键字,会暂停其他代码的执行。当**await**关键字引用的**Future**执行完成(也就是执行**print("后面执行这句话),_method_C()方法会立即执行,然后执行继续执行这句哈11111~,最后执行print(“start结束”);

3.Stream

Stram是接收异步事件数据,和Future不同的是,它可以接收多个异步操作的结果,那么Stram常用于在多次读取数据的异步任务场景,直接上例子:

void create(){

Stream.fromFutures([
//2秒后返回结果
Future.delayed(new Duration(seconds: 2),(){
return “Android”;
}),

//3秒后抛出一个异常
Future.delayed(new Duration(seconds: 3),(){
return AssertionError(“error”);
}),

//4秒后返回结果
Future.delayed(new Duration(seconds: 4),(){
return “Flutter”;
})

]).listen((result){
//打印接收的结果
print(result);
},onError: (e){
//错误回调
print(e.message);

},onDone: (){

});

}

上面可以发现Stream可以通过触发成功或者失败传递结果或者错误。

四、文件操作

有很多时候需要将文件保存到本地,这时候就需要用文件读写接口来实现,PathProvider插件提供一种平台透明的方式来访问设备文件系统上的常用位置。该类当前支持两个文件系统位置:

  • 临时目录:系统可随时清除的临时目录(缓存)。在iOS上,这对应于NSTemporaryDirectory()返回的值。在Android上,这是getCacheDir()返回的值。
  • 文档目录:应用程序的目录,用于存储只有自己可以访问的文件,只有当应用程序被卸载时,系统才会清除目录。在iOS上,这对应于NSDocumentDirectory。在Android上,这是AppData目录。

Flutter里实现文件读写,需要使用path_providerDart里的I/O模块,两者的职责并不一样,path_provider是负责查找iOS或者Android下的目录文件,而I/O是负责文件的读写操作。

1.获取本地路径

下面使用path_provider来查找本地的路径,首先在pubspec.xml文件添加依赖:

dependencies:
flutter:
sdk: flutter

The following adds the Cupertino Icons font to your application.

Use with the CupertinoIcons class for iOS style icons.

path_provider: ^0.4.1 -->添加依赖

或者临时目录,文档目录,sd卡目录如下:

import ‘dart:io’;
import ‘dart:async’;
import ‘package:flutter/material.dart’;
import ‘package:path_provider/path_provider.dart’;

class _LoadFileState extends State{
@override
void initState(){
super.initState();
}

@override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: new Text(“LoadFile”),
),
body: new Center(
child: RaisedButton(
child: Text(“获取文件路径”),
//点击调用获取文件路径方法
onPressed: loadPath,
),
),
);
}
}

loadPath() async{
try{
//临时目录
var _tempDir = await getTemporaryDirectory();
//获取具体路径
String tempDirPath = _tempDir.path;
//文档目录
var _document = await getApplicationDocumentsDirectory();
String documentPath = _document.path;
//sd卡目录
var _sdCard = await getExternalStorageDirectory();
String sdCardPath = _sdCard.path;

//打印路径
print(“临时目录:”+ tempDirPath);
print(“文档目录:”+ documentPath);
print(“sd卡目录:”+ sdCardPath);

}catch(err){
print(err);
}
}

输出结果(Android)如下:

I/flutter (19375): 临时目录:/data/user/0/com.example.loadflie/cache
I/flutter (19375): 文档目录:/data/user/0/com.example.loadflie/app_flutter
I/flutter (19375): sd卡目录:/storage/emulated/0

2.读取本地文件内容

读取文件少不了权限的问题,在Dart Packages可以找到simple_permissions这个库来简化申请权限的步骤,按照上面说明跟着操作就可以:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的意思是在AndroidManifestInfo.plist文件下添加权限,身为Android coder对AndroidManifest这个文件很熟悉,这个文件是对Android而言,而Info.plist应该是对于iOS而言,那下面先在Android上试试看,首先,在pubspec.yaml上添加依赖:

simple_permissions: ^0.1.9

记得点击Packages get命令。 接着在AndroidManifest清单文件上添加对文件的读写权限:

下面在手机sd card内部存储新建一个txt文件,尝试获取其内容:

import ‘package:simple_permissions/simple_permissions.dart’;//记得加上这句话

//读取文件方法
readData() async {
try {
//申请读文件的权限
var permission =
SimplePermissions.requestPermission(Permission.ReadExternalStorage);
var sdCardPath = getExternalStorageDirectory();
//当获取到路径的时候
sdCardPath.then((filePath) {
//获得读取文件权限
permission.then((permission_status) async {
//获取文件内容
var data = await File(filePath.path + “/flutter.txt”).readAsString();
print(data);
});
});
} catch (e) {
print(e);
}
}

按钮点击方法改为readData:

child: RaisedButton(
child: Text(“获取文件路径”),
onPressed: readData
),

点击按钮结果运行:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选择始终运行: 输出就是flutter.txt文件内容:

I/flutter (24038): flutter is very good.

注意如果不加读写权限,会抛出异常:

I/flutter (25428): FileSystemException: Cannot open file, path = ‘/storage/emulated/0/flutter.txt’ (OS Error: Permission denied, errno = 13)

3.写入文件操作

//把内容写入文件操作
writeData() async{
try {
//申请读文件的权限
var permission =
SimplePermissions.requestPermission(Permission.WriteExternalStorage);
var sdCardPath = getExternalStorageDirectory();
//当获取到路径的时候
sdCardPath.then((filePath) {
//获得读取文件权限
permission.then((permission_status) async {
//把内容写进文件
var data = await File(filePath.path + “/flutter.txt”).writeAsString(“点滴之行,看世界”);
print(data);
});
});
} catch (e) {
print(e);
}
}

打开sd card的flutter.txt文件看看内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

发现,把之前的内容覆盖了!那么如何实现所写入的内容不覆盖原来文件的内容呢?这时候需要用到append模式,很简单,把默认的FileMode mode: FileMode.write方式改为FileMode mode: FileMode.append,代码如下:

//把内容写进文件 现在以追加的方式
var data = await File(filePath.path + “/flutter.txt”).writeAsString(“Flutter is very good”,
mode: FileMode.append);

运行结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

好了,简单的读写文件就实现了。

五、sqflite数据库

AndroidiOS中都会有SQLite,那么Flutter有没有呢?答案是肯定有的。Flutter中的SQLite数据库是同时支持AndroidiOS的,它的名字叫sqflite ,支持事务和批量操作,支持插入/查询/更新/删除操作等,是轻量级的关系型数据库。 下面先简单实现一个登录界面,进行简单的数据操作:

//用无状态控件显示
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
//主题色
theme: ThemeData(
//设置为蓝色
primarySwatch: Colors.red),
//这是一个Widget对象,用来定义当前应用打开的时候,所显示的界面
home: DataBaseWidget(),
);
}
}

//主框架
class DataBaseWidget extends StatefulWidget {
@override
State createState() {
return new _DataBaseState();
}
}

class _DataBaseState extends State {
@override
Widget build(BuildContext context) {
return new Scaffold(
//appBar
appBar: AppBar(
title: Text(“Sqlite简单操作”),
//标题居中
centerTitle: true,
),
body: new ListView(
children: [
//用户输入用户信息widget
Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: InputMessageWidget(),
),
//数据库表的一些基本操作,增,删,改,查
Padding(
padding: const EdgeInsets.all(16),
child: SqliteHandleWidget(),
),
],
),
);
}
}

用户输入信息的Widget如何:

//用户名和密码
class InputMessageWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
//这个是为了用户输入结束后,让密码输入框获取到焦点
FocusNode secondTextFieldNode = FocusNode();
return Column(
children: [
TextField(
//文字内容改变触发
onChanged: (user) {
//获取用户名
username = user;
},
//输入法装饰器
decoration: InputDecoration(
//标签
labelText: ‘名字’,
//hint 提示用户输入什么
hintText: ‘请输入英文或者数字’),
//最大为一行
maxLines: 1,
//文字提交触发
onSubmitted: (result) {
FocusScope.of(context).reparentIfNeeded(secondTextFieldNode);
},
),
TextField(
onChanged: (pwd) {
//获取用户密码
password = pwd;
},
//是否隐藏输入 false 表示不隐藏,true表示隐藏
obscureText: true,
maxLines: 1,
decoration: InputDecoration(
labelText: ‘密码’,
hintText: ‘请输入密码’,
),
//键盘输入类型
keyboardType: TextInputType.text,
onSubmitted: (data) {},
),
],
);
}
}

对数据库表操作的按钮布局如下:

//数据库组件操作
class SqliteHandleWidget extends StatefulWidget {
@override
State createState() {
return new _SqliteHandleWidgetState();
}
}

class _SqliteHandleWidgetState extends State {
//数据库名称
String myDataBase = “usermessage.db”;

//数据库路径
String myDataBasePath = “”;

//数据库中的表 简单一点,就创建三个字段,分别是主键,用户名,密码
String sql_createUserTable = “CREATE TABLE user(”
“id INTEGER PRIMARY KEY,”
“username TEXT,”
“password TEXT)”;

//查找数据库表的数目
String sql_queryCount = ‘SELECT COUNT(*) FROM user’;

//具体查找数据库表的所有信息
String sql_queryMessage = ‘SELECT * FROM user’;

//这是从数据库表返回数据
var _data;

@override
Widget build(BuildContext context) {
return Column(
//交叉轴设置中间
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 40.0,
child: RaisedButton(
textColor: Colors.black,
child: Text(“创建数据库表”),
onPressed: null,
),
),
Row(
//主轴方向中心对齐
mainAxisAlignment: MainAxisAlignment.center,
children: [
new RaisedButton(
textColor: Colors.black,
child: new Text(‘增’),
onPressed: null),
new RaisedButton(
textColor: Colors.black,
child: new Text(‘删’),
onPressed: null),
new RaisedButton(
textColor: Colors.black,
child: new Text(‘改’),
onPressed: null),
],
),
Row(
//主轴方向中心对齐
mainAxisAlignment: MainAxisAlignment.center,
children: [
new RaisedButton(
textColor: Colors.black,
child: new Text(‘查条数’),
onPressed: null),
new RaisedButton(
textColor: Colors.black,
child: new Text(‘查信息’),
onPressed: null),
],
),
Padding(
padding: const EdgeInsets.all(16.0),
child: new Text(‘具体结果是:$_data’),
),
],
);
}
}

在上面_SqliteHandleWidgetState赋值数据库名字为usermessage.db,创建数据库表user语句很简单,就三个字段,分别是主键,用户名,用户密码,界面如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

界面弄好了,下面就一步一步来。

1.创建数据库和数据表

首先添加依赖:可以到Dart包管理网站去查找sqlite依赖最新版本。

sqflite: ^1.1.0

并在文件引入:

import ‘package:path_provider/path_provider.dart’;
import ‘dart:io’;
import ‘package:sqflite/sqflite.dart’;
import ‘package:path/path.dart’;

注意:对于数据库的操作都是耗时操作,都要通过异步来处理。

//创建数据库
Future createDataBase(String db_name) async {
//在文档目录建立
var document = await getApplicationDocumentsDirectory();
//获取路径 join是path包下的方法,就是将两者路径连接起来
String path = join(document.path, db_name);
//逻辑是如果数据库存在就把它删除然后创建
var _directory = new Directory(dirname(path));
bool exists = await _directory.exists();
if (exists) {
//必存在 这里是为了每次创建数据库表先表删除则删除数据库表
await deleteDatabase(path);
} else {
try {
//不存在则创建目录 如果[recursive]为false,则只有路径中的最后一个目录是
//创建。如果[recursive]为真,则所有不存在的路径
//被创建。如果目录已经存在,则不执行任何操作。
await new Directory(dirname(path)).create(recursive: true);
} catch (e) {
print(e);
}
}
return path;
}

//创建数据库表方法
cratedb_table() async {
//得到数据库的路径
myDataBasePath = await createDataBase(myDataBase);
//打开数据库
Database my_db = await openDatabase(myDataBasePath);
//创建数据库表
await my_db.execute(sql_createUserTable);
//关闭数据库
await my_db.close();
setState(() {
_data = “创建usermessage.db成功,创建user表成功~”;
});
}

给按钮添加点击方法:

child: RaisedButton(
textColor: Colors.black,
child: Text(“创建数据库表”),
onPressed: cratedb_table,
),

运行,安装完apk,用Device File Exploder来看看内部存储文件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面点击创建数据库,后synchronize来刷新一下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

发现在app_flutter下多了usermessage.db文件,确实数据库创建成功了,那继续下面的操作。

2.增加数据

下面实现增加数据,可以用rawInsert或者db.insert方式对数据库表数据进行增加(插入),实际上都是通过insert into方式来插入数据表,下面就用rawInsert方式来增加一条数据:

//增加方法
addData() async {
//首先打开数据库
Database my_db = await openDatabase(myDataBasePath);
//插入数据
String add_sql = “INSERT INTO user(username,password) VALUES(‘ u s e r n a m e ′ , ′ username',' username,password’)”;
await my_db.transaction((tran) async{
await tran.rawInsert(add_sql);
});
//关闭数据库
await my_db.close();
setState(() {
_data = “增加一条数据成功,名字是: u s e r n a m e , 密码是: username,密码是: username,密码是:password”;
});
}

3.查询具体数据

为了配合增加数据,把查询数据库表的功能实现:

//查询具体数值
queryDetail() async{
//打开数据库
Database my_db = await openDatabase(myDataBasePath);
//将数据放到集合里面显示
List dataList = await my_db.rawQuery(sql_queryMessage);
await my_db.close();
setState(() {
_data = “具体数据详情如下:$dataList”;
});
}

查询数据表很简单,实际上只用rawQuery这个方法,把增加和查询方法绑定到按钮点击上:

new RaisedButton(
textColor: Colors.black, child: new Text(‘改’), onPressed: null),

new RaisedButton(
textColor: Colors.black,
child: new Text(‘查信息’),
onPressed: queryDetail),

验证结果,流程是:

  1. 先输入用户名和密码
  2. 点击增加
  3. 点击查信息: 运行结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.删除数据

下面实现删除数据:

//删除一条数据
delete() async {
Database my_db = await openDatabase(myDataBasePath);
//根据id来删除 也可以根据其他信息来删除 例如名字
String delete_ssql = “DELETE FROM user WHERE id = ?”;
//返回所更改的数目
int delete_count = await my_db.rawDelete(delete_ssql,[‘1’]);
//关闭数据库
await my_db.close();
//状态更新
setState(() {
if(delete_count == 1){
_data = “删除成功~”;
} else {
_data = “删除失败,请看错误日志~”;
}
});
}

记得给删除按钮绑定方法,运行结果就不贴了。

5.修改数据

修改数据我相信在平时开发中是用的最频繁的操作了,直接上实现例子:

//修改数据方法
update() async{
//数据库
Database my_db = await openDatabase(myDataBasePath);
String update_sql = “UPDATE user SET username = ? WHERE id = ?”;
await my_db.rawUpdate(update_sql,[‘paul’,‘1’]);
await my_db.close();

setState(() {
_data = “数据修改成功,请查阅~”;
});

}

上面用了rawUpdate对数据库表进行内容数据更新,也可以用db.update来更新,自己可以根据需求变更去修改固定字段或者整条数据。上面我是根据id这个条件来修改一条数据,将id为1的数据的名字改为paul

6.查询条数

//查询有几条
query_num() async{
//数据库
Database my_db = await openDatabase(myDataBasePath);
//用sqflite包的方法firstInValue
int data_count = Sqflite.firstIntValue(await my_db.rawQuery(sql_queryCount));
await my_db.close();
setState(() {
_data = “数据条数:$data_count”;
});
}

对本地数据库的基本操作实现了一遍,下面学习网络请求操作。

六、网络请求操作

Flutter的请求网络有多种方式,一种是使用dart io中的HttpClient发起的请求,一种是使用dio库,另一种是使用http库,先学一下getpostputdelete就等后面用到在学。下面就实践:

1.dart io发起的请求

1.1.get请求

import ‘dart:io’;//导IO包
import ‘dart:convert’;//解码和编码JSON
void main() {
_get();
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

愿你有一天,真爱自己,善待自己。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
t:io’;//导IO包
import ‘dart:convert’;//解码和编码JSON
void main() {
_get();
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-FcGfFV8E-1712786099713)]
[外链图片转存中…(img-4kPjunPW-1712786099713)]
[外链图片转存中…(img-bN0eJKHf-1712786099713)]
[外链图片转存中…(img-D8msr3D1-1712786099714)]
[外链图片转存中…(img-zSw0rfUP-1712786099714)]
[外链图片转存中…(img-F7o0oh13-1712786099714)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-5JSBxgj6-1712786099714)]

最后

愿你有一天,真爱自己,善待自己。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-BlIUGUu8-1712786099714)]

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值