我有一个 Flutter 应用程序,用户可以使用
dropdown_search
包从下拉列表中选择多个选项。我想添加一个功能,将所选项目缓存在本地,然后在用户返回应用程序时预先填充。
我尝试使用
shared_preferences
包将所选项目存储在设备的本地存储中。但是,当我尝试在 onItemAdded
小部件的 onItemRemoved
和 DropdownSearch
回调中异步保存所选选项时遇到错误。
这是代码:
import 'package:flutter/material.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'MultiSelectDropDown',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'MultiSelectDropDown'),
initialBinding: QuestionnaireBinding(),
);
}
}
class QuestionnaireBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<QuestionnaireController>(() => QuestionnaireController());
}
}
class QuestionnaireController extends GetxController
with GetSingleTickerProviderStateMixin {
RxList<String> choices = <String>[].obs;
Rx<bool> showBackButton = true.obs;
@override
void onInit() {
super.onInit();
loadSelectedChoices();
}
Future<void> saveSelectedChoices() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setStringList('selected_choices', choices.toList());
}
Future<void> loadSelectedChoices() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String>? loadedChoices = prefs.getStringList('selected_choices');
if (loadedChoices != null) {
print("loading choices");
choices.value = loadedChoices.obs;
}
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final dropdownSearchTheme = ThemeData(
colorScheme: const ColorScheme(
primary: Color(0xFF485946),
onPrimary: Colors.white,
secondary: Colors.black,
onSecondary: Colors.deepOrange,
error: Colors.transparent,
onError: Colors.transparent,
background: Colors.transparent,
onBackground: Colors.black,
brightness: Brightness.light,
onSurface: Colors.black,
surface: Colors.white,
));
@override
Widget build(BuildContext context) {
return GetBuilder<QuestionnaireController>(builder: (controller) {
return Scaffold(
appBar: AppBar(
leading: Obx(() => controller.showBackButton.value ? BackButton() : SizedBox.shrink()),
// Use the new variable to control the visibility of the back button
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Text(
'Let\'s start choosing',
textAlign: TextAlign.center,
),
const SizedBox(
height: 30,
),
Theme(
data: dropdownSearchTheme,
child: _buildDropdownSearch(
context,
Get.find<QuestionnaireController>().choices,
dropdownCustomBuilder: _customChoice,
),
),
],
),
),
),
);
});
}
Widget _buildDropdownSearch(
BuildContext context, RxList<String> selectedChoice,
{Widget Function(BuildContext, List<String>)? dropdownCustomBuilder}) {
final controller = Get.find<QuestionnaireController>();
// Initialize FocusNode and add a listener
FocusNode _dropdownFocusNode = FocusNode();
_dropdownFocusNode.addListener(() {
if (_dropdownFocusNode.hasFocus) {
// Dropdown is opened
controller.showBackButton.value = false;
} else {
controller.showBackButton.value = true;
}
});
return DropdownSearch<String>.multiSelection(
selectedItems: selectedChoice,
items: [
"choice1",
"choice2",
"choice3",
"choice4",
"choice5",
"choice6",
"choice7",
"choice8",
"choice9",
"choice10",
"choice11"
],
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
hintText: 'pickChoice',
hintStyle: TextStyle(color: Colors.black),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Colors.grey,
),
),
)),
dropdownBuilder: dropdownCustomBuilder,
popupProps: PopupPropsMultiSelection.bottomSheet(
searchFieldProps: TextFieldProps(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
decoration: InputDecoration(
hintText: 'Search for choice',
hintStyle: TextStyle(color: Colors.black),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Colors.grey,
),
),
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
controller.choices.clear();
},
),
),
style: TextStyle(color: Colors.black),
autofocus: true,
focusNode: _dropdownFocusNode,
),
showSearchBox: true,
showSelectedItems: true,
isFilterOnline: false,
onItemAdded: (selectedItems, addedItem) {
selectedChoice.add(addedItem);
controller.saveSelectedChoices();
},
onItemRemoved: (selectedItems, removedItem) {
selectedChoice.remove(removedItem);
controller.saveSelectedChoices();
},
),
);
}
Widget _customChoice(BuildContext context, List<String> selectedItems) {
final controller = Get.find<QuestionnaireController>();
return GetBuilder<QuestionnaireController>(
initState: (_) {},
builder: (_) {
if (selectedItems.isEmpty) {
return Text("Pick a choice");
}
return Wrap(
spacing: 2,
runSpacing: -10,
children: selectedItems.map((e) {
return Chip(
backgroundColor: Colors.lightGreen,
label: Text(
e.toString().trim(),
),
onDeleted: () {
selectedItems.remove(e.toString().trim());
controller.choices.remove(e.toString().trim());
},
);
}).toList(),
);
},
);
}
}
我收到的错误是:
Lookup failed: SharedPreferences in package:shared_preferences/shared_preferences.dart
我知道回调不是为处理异步操作而设计的,因此我正在寻找实现此功能的最佳方法的指导。
如何正确缓存 Flutter 中多选下拉列表中的所选项目,并在用户返回应用程序时加载它们?
在应用程序启动之前加载您的数据:
void main() async { // <= async main
final preferences = await SharedPreferences.getInstance();
final data = ... // get your saved data
runApp(MyApp(data: data)); // pass data from preferences to your app
}
回调不必
await
保存到首选项:触发并忘记您的保存操作。