Flutter_学习记录_本地存储数据
背景
项目中经常会有本地存储数据的需求,例如,本地存储搜索页面“搜索数据”, 如下图:
思路
- 进入页面时,加载本地数据
- 点击搜索时,将搜索数据存储到本地:先读取本地数据,如果有数据,就追加数据,并保存;如果本地没数据,就直接保存。
- 点击“清空所有历史记录”时,删除本地的搜索记录的数据
- 长按某一条数据时,展示“确认删除”弹框,删除某条数据。
代码实现
-
用三方库
shared_preferences
来实现,在项目中引入shared_preferences
库:
-
创建一个
StorageService
类,来封装对应的方法,代码如下:
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class StorageService {
static final _searchKey = "SearchHistory";
// 获取搜索的历史记录
static Future<List<String>> getSearchHistoryString() async {
final shareInstance = await SharedPreferences.getInstance();
try {
final historyString = shareInstance.getString(_searchKey);
print("搜索历史记录 = $historyString");
if (historyString != null) {
List historyList = json.decode(historyString);
return historyList.map((value){
return "$value";
}).toList();
} else {
print("搜索历史记录 没有数据");
return [];
}
} catch (e) {
print("搜索历史记录 没有数据");
return [];
}
}
/*
存储搜索历史记录
如果有数据,就追加数据,然后存储;
如果没有数据,就新建List,添加数据,然后存储;
*/
static Future<void> setSearchHistoryString(String keyword) async {
final shareInstance = await SharedPreferences.getInstance();
try {
final historyString = shareInstance.getString(_searchKey);
if (historyString != null) {
List historyList = json.decode(historyString);
historyList.add(keyword);
final dataString = json.encode(historyList);
print("历史记录 = $dataString");
shareInstance.setString(_searchKey, dataString);
} else {
List historyList = [keyword];
final dataString = json.encode(historyList);
print("历史记录 = $dataString");
shareInstance.setString(_searchKey, dataString);
}
} catch (e) {
List historyList = [keyword];
final dataString = json.encode(historyList);
print("历史记录 = $dataString");
shareInstance.setString(_searchKey, dataString);
}
}
// 删除某条历史记录
static Future<List<String>> removeSearchKeyword(String keyword) async {
final shareInstance = await SharedPreferences.getInstance();
try {
final historyString = shareInstance.getString(_searchKey);
if (historyString != null) {
List historyList = json.decode(historyString);
List<String> historyStringList = historyList.map((value){
return "$value";
}).toList();
historyStringList.remove(keyword);
final dataString = json.encode(historyStringList);
print("历史记录 = $dataString");
shareInstance.setString(_searchKey, dataString);
return historyStringList;
} else {
return [];
}
} catch (e) {
return [];
}
}
// 清空搜索历史记录
static Future<void> clearSearchHistory() async {
final shareInstance = await SharedPreferences.getInstance();
try {
shareInstance.remove(_searchKey);
} catch (e) {
print(e);
}
}
}
- 创建删除的提示框:
import 'dart:ffi';
import 'package:flutter/material.dart';
class ShowAlertWidget {
/// 显示一个带有标题和内容的对话框
///
/// 参数:
/// - `context`: 当前上下文
/// - `title`: 对话框标题
/// - `content`: 对话框内容
///
/// 返回值:
/// - 0: 用户点击了 "取消"
/// - 1: 用户点击了 "确定"
static Future<int> showAlert(BuildContext context, String title, String content) async {
final value = await showDialog<int>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: Text(content),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context, 0); // 返回值为 0,表示取消
},
child: Text("取消"),
),
TextButton(
onPressed: () {
Navigator.pop(context, 1); // 返回值为 1,表示确定
},
child: Text("确定"),
),
],
);
},
) ?? 0; // 如果用户直接关闭对话框,则默认返回 0(取消)
print("value = $value");
return value;
}
}
- 使用的地方:
//
- [ 进入页面的时候,加载数据]
void initState() {
super.initState();
// 进入页面的时候,加载数据
StorageService.getSearchHistoryString().then((list){
setState(() {
_historySearchData = list;
});
});
}
- 点击搜索时,将搜索数据存储到本地
onTap: (){
if (_searchTextValue.isEmpty) {
return;
}
// 存储数据
StorageService.setSearchHistoryString(_searchTextValue);
// 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
Navigator.pushReplacementNamed(context, "/productList", arguments: {
"keyword": _searchTextValue
});
}
- 点击“清空所有历史记录”时,删除本地的搜索记录的数据
onTap: (){
StorageService.clearSearchHistory();
setState(() {
_historySearchData = [];
});
}
- 长按某一条数据时,展示“确认删除”弹框,删除某条数据
onLongPress: (){
ShowAlertWidget.showAlert(context, "删除提醒", "确认要删除这条数据吗").then((value) {
if (value == 1) {
StorageService.removeSearchKeyword(keyword).then((list){
setState(() {
_historySearchData = list;
});
});
}
});
}
完整代码案例
import 'package:fangjd/CommonWidget/ShowAlertWidget.dart';
import 'package:fangjd/Services/ScreenAdapter.dart';
import 'package:fangjd/Services/StorageService.dart';
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
const SearchPage({super.key});
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
final List<String> _hotSearchData = ["超级秒杀", "办公电脑", "儿童汽车", "唇彩唇蜜", "哪吒2"];
List<String> _historySearchData = ["贝亲奶瓶", "纸尿裤", "婴儿衣架", "包被"];
String _searchTextValue = "";
// 设置搜索条
Widget _searchInputWidget() {
return Container(
padding: EdgeInsets.only(left: Screenadapter.width(20)),
height: Screenadapter.height(80),
decoration: BoxDecoration(
color: Color.fromRGBO(233, 233, 233, 0.9),
borderRadius: BorderRadius.circular(Screenadapter.height(40)),
),
child: TextField(
autofocus: false,
decoration: InputDecoration(
// 设置内容的间距
contentPadding: EdgeInsets.all(10),
// 设置图标
prefixIcon: Icon(Icons.search, size: 18, color: Colors.black45),
// 设置前置图标的约束
prefixIconConstraints: BoxConstraints(
//添加内部图标之后,图标和文字会有间距,实现这个方法,不用写任何参数即可解决
minWidth: 30.0
),
// 设置提示文字
hintText: "请输入你想搜索的商品",
// 设置提示文字的样式
hintStyle: TextStyle(fontSize: 16, color: Colors.black45),
// 去掉输入框底部的线
border: OutlineInputBorder(
borderSide: BorderSide.none,
),
),
onChanged: (value) {
_searchTextValue = value;
},
),
);
}
// 设置搜索的按钮
Widget _searchItemWiget() {
return InkWell(
onTap: (){
if (_searchTextValue.isEmpty) {
return;
}
// 存储数据
StorageService.setSearchHistoryString(_searchTextValue);
// 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
Navigator.pushReplacementNamed(context, "/productList", arguments: {
"keyword": _searchTextValue
});
},
child: Container(
padding: EdgeInsets.only(right: Screenadapter.width(30)),
height: Screenadapter.height(80),
width: Screenadapter.width(100),
child: Center(
child: Text("搜索", style: TextStyle(fontSize: 18)),
),
),
);
}
// 热搜的视图
Widget _hotSearchWidget() {
return SizedBox(
height: Screenadapter.height(180),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SizedBox(width: 10),
Text("热搜", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w800))
],
),
SizedBox(height: 10),
SingleChildScrollView(
padding: EdgeInsets.only(left: 10),
scrollDirection: Axis.horizontal,
child: Row(
children: _hotSearchData.map((value){
return InkWell(
onTap: (){
// 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
Navigator.pushReplacementNamed(context, "/productList", arguments: {
"keyword": value
});
},
child: Container(
height: 30,
padding: EdgeInsets.only(left: 10, right: 10),
margin: EdgeInsets.only(right: 10),
color: Colors.grey[200],
child: Center(child: Text(value)),
),
);
}).toList(),
),
),
SizedBox(height: 10),
Container(
height: 15,
color: Colors.grey[200],
)
],
),
);
}
// 历史搜索
Widget _historSearchTitleWidget() {
return SizedBox(
height: 50,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
padding: EdgeInsets.only(top: 14, bottom: 14, left: 10),
child: Text("历史搜索", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w800), textAlign: TextAlign.start),
)),
Divider(height: 1, color: Colors.grey[200])
],
),
);
}
// 搜索页面的内容视图
Widget _searchHotAndHistroyWidget() {
return ListView.builder(
itemBuilder: (context, index){
if (index == 0) {
return _hotSearchWidget();
}
if (index == 1) {
return _historSearchTitleWidget();
}
final keyword = _historySearchData[index -2];
return Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: (){
// 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
Navigator.pushReplacementNamed(context, "/productList", arguments: {
"keyword": keyword
});
},
onLongPress: (){
ShowAlertWidget.showAlert(context, "删除提醒", "确认要删除这条数据吗").then((value) {
if (value == 1) {
StorageService.removeSearchKeyword(keyword).then((list){
setState(() {
_historySearchData = list;
});
});
}
});
},
child: Container(
width: double.infinity,
padding: EdgeInsets.only(top: 14, bottom: 14),
child: Text(keyword, style: TextStyle(fontSize: 16), textAlign: TextAlign.start),
),
),
Divider(height: 1, color: Colors.grey[200])
],
),
);
},
itemCount: _historySearchData.isNotEmpty ? _historySearchData.length + 2 : 1,
);
}
// 删除的按钮
Widget _deleteHistoryWiget() {
return Container(
margin: EdgeInsets.only(left: Screenadapter.width(60), right: Screenadapter.width(60)),
height: Screenadapter.height(100),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.black45
),
borderRadius: BorderRadius.circular(5)
),
child: InkWell(
onTap: (){
StorageService.clearSearchHistory();
setState(() {
_historySearchData = [];
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.delete),
Text("清空所有历史记录")
],
),
),
);
}
void initState() {
super.initState();
StorageService.getSearchHistoryString().then((list){
setState(() {
_historySearchData = list;
});
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _searchInputWidget(),
actions: [
_searchItemWiget()
],
),
body: Container(
padding: EdgeInsets.only(bottom: 30),
child: Column(
children: [
Expanded(child: _searchHotAndHistroyWidget()),
_deleteHistoryWiget()
],
),
),
);
}
}