如何在 Flutter 中缓存多选下拉列表中的所选项目

问题描述 投票:0回答:1

我有一个 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 中多选下拉列表中的所选项目,并在用户返回应用程序时加载它们?

flutter dropdown flutter-sharedpreference
1个回答
0
投票

在应用程序启动之前加载您的数据:

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
保存到首选项:触发并忘记您的保存操作。

© www.soinside.com 2019 - 2024. All rights reserved.