Флаттер CheckedPopupMenuItem Оставьте меню открытым после выбора элемента

Я пытаюсь, чтобы меню оставалось открытым, чтобы я мог выбрать несколько категорий одновременно, не возвращаясь к странице после каждого выбора.

Было бы идеально, если бы он работал как фильтр данных Excel (или Libreoffice Autofilter)

добавлены скриншоты

Любые идеи были бы хорошы!

текущее приложение пример моей цели

код:

import 'package:flutter/material.dart';

void main() {   return runApp(MaterialApp(
    home: MenuDemo(),   )); }

class MenuDemo extends StatefulWidget {   const MenuDemo({Key key}) : super(key: key);

  static const String routeName = '/material/menu';

  @override   MenuDemoState createState() => new MenuDemoState(); }

class MenuDemoState extends State<MenuDemo> {   final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

  final String _checkedValue1 = 'One';   final String _checkedValue2 = 'Two';   final String _checkedValue3 = 'Free';   final String
_checkedValue4 = 'Four';   List<String> _checkedValues;

  @override   void initState() {
    super.initState();
    _checkedValues = <String>[_checkedValue3];   }

  void showInSnackBar(String value) {
    _scaffoldKey.currentState
        .showSnackBar(new SnackBar(content: new Text(value)));   }

  void showCheckedMenuSelections(String value) {
    if (_checkedValues.contains(value))
      _checkedValues.remove(value);
    else
      _checkedValues.add(value);

    showInSnackBar('Checked $_checkedValues');   }

  bool isChecked(String value) => _checkedValues.contains(value);

  @override   Widget build(BuildContext context) {
    return new Scaffold(
        key: _scaffoldKey,
        body: Center(
          child: Container(
            width: 250.0,
            child: new ListTile(
                title: const Text(
                  'checklist menu',
                  textAlign: TextAlign.center,
                ),
                trailing: new PopupMenuButton<String>(
                    padding: EdgeInsets.zero,
                    onSelected: showCheckedMenuSelections,
                    itemBuilder: (BuildContext context) =>
                        <PopupMenuItem<String>>[
                          new CheckedPopupMenuItem<String>(
                              value: _checkedValue1,
                              checked: isChecked(_checkedValue1),
                              child: new Text(_checkedValue1)),
                          new CheckedPopupMenuItem<String>(
                              value: _checkedValue2,
                              //enabled: false,
                              checked: isChecked(_checkedValue2),
                              child: new Text(_checkedValue2)),
                          new CheckedPopupMenuItem<String>(
                              value: _checkedValue3,
                              checked: isChecked(_checkedValue3),
                              child: new Text(_checkedValue3)),
                          new CheckedPopupMenuItem<String>(
                              value: _checkedValue4,
                              checked: isChecked(_checkedValue4),
                              child: new Text(_checkedValue4))
                        ])),
          ),
        ));   } }

1 ответ

Вы можете создать свое собственное всплывающее меню с множественным выбором, поскольку использование PopupMenu по умолчанию закрывает диалоговое окно при выборе элемента.

      class MultiSelectDialogItem<V> {
  const MultiSelectDialogItem(this.value, this.label);

  final V value;
  final String label;
}

class MultiSelectDialog<V> extends StatefulWidget {
  const MultiSelectDialog(
      {Key? key, required this.items, this.initialSelectedValues})
      : super(key: key);

  final List<MultiSelectDialogItem<V>> items;
  final Set<V>? initialSelectedValues;

  @override
  State<StatefulWidget> createState() => _MultiSelectDialogState<V>();
}

class _MultiSelectDialogState<V> extends State<MultiSelectDialog<V>> {
  final _selectedValues = <V>{};

  @override
  void initState() {
    super.initState();
    if (widget.initialSelectedValues != null) {
      _selectedValues.addAll(widget.initialSelectedValues!);
    }
  }

  void _onItemCheckedChange(V itemValue, bool checked) {
    setState(() {
      if (checked) {
        _selectedValues.add(itemValue);
      } else {
        _selectedValues.remove(itemValue);
      }
    });
  }

  void _onCancelTap() {
    Navigator.pop(context);
  }

  void _onSubmitTap() {
    Navigator.pop(context, _selectedValues);
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Select items'),
      contentPadding: const EdgeInsets.only(top: 12.0),
      content: SingleChildScrollView(
        child: ListTileTheme(
          contentPadding: const EdgeInsets.fromLTRB(14.0, 0.0, 24.0, 0.0),
          child: ListBody(
            children: widget.items.map(_buildItem).toList(),
          ),
        ),
      ),
      actions: <Widget>[
        ElevatedButton(
          child: const Text('Cancel'),
          onPressed: _onCancelTap,
        ),
        ElevatedButton(
          child: const Text('Ok'),
          onPressed: _onSubmitTap,
        )
      ],
    );
  }

  Widget _buildItem(MultiSelectDialogItem<V> item) {
    final checked = _selectedValues.contains(item.value);
    return CheckboxListTile(
      value: checked,
      title: Text(item.label),
      controlAffinity: ListTileControlAffinity.leading,
      onChanged: (checked) =>
          _onItemCheckedChange(item.value, checked ?? false),
    );
  }
}

Затем, чтобы отобразить меню.

      void _showMultiSelect(BuildContext context) async {
  // set value and label on MultiSelectDialogItem 
  final items = <MultiSelectDialogItem<int>>[
    const MultiSelectDialogItem(1, 'Item 1'),
    const MultiSelectDialogItem(2, 'Item 2'),
    const MultiSelectDialogItem(3, 'Item 3'),
  ];

  final selectedValues = await showDialog<Set<int>>(
    context: context,
    builder: (BuildContext context) {
      return MultiSelectDialog(
        items: items,
        // use Set to configure initial selected value
        // i.e. initialSelectedValues: const {1,2}  
        initialSelectedValues: null,
      );
    },
  );

  // Fetch selected items
  debugPrint('Selected Values: $selectedValues');
}

Полный образец

      import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _showMultiSelect(BuildContext context) async {
    final items = <MultiSelectDialogItem<int>>[
      const MultiSelectDialogItem(1, 'Item 1'),
      const MultiSelectDialogItem(2, 'Item 2'),
      const MultiSelectDialogItem(3, 'Item 3'),
    ];

    final selectedValues = await showDialog<Set<int>>(
      context: context,
      builder: (BuildContext context) {
        return MultiSelectDialog(
          items: items,
          initialSelectedValues: null,
        );
      },
    );

    debugPrint('Selected Values: $selectedValues');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _showMultiSelect(context);
          },
          child: const Text('Show Dropdown Multiselect'),
        ),
      ),
    );
  }
}

class MultiSelectDialogItem<V> {
  const MultiSelectDialogItem(this.value, this.label);

  final V value;
  final String label;
}

class MultiSelectDialog<V> extends StatefulWidget {
  const MultiSelectDialog(
      {Key? key, required this.items, this.initialSelectedValues})
      : super(key: key);

  final List<MultiSelectDialogItem<V>> items;
  final Set<V>? initialSelectedValues;

  @override
  State<StatefulWidget> createState() => _MultiSelectDialogState<V>();
}

class _MultiSelectDialogState<V> extends State<MultiSelectDialog<V>> {
  final _selectedValues = <V>{};

  @override
  void initState() {
    super.initState();
    if (widget.initialSelectedValues != null) {
      _selectedValues.addAll(widget.initialSelectedValues!);
    }
  }

  void _onItemCheckedChange(V itemValue, bool checked) {
    setState(() {
      if (checked) {
        _selectedValues.add(itemValue);
      } else {
        _selectedValues.remove(itemValue);
      }
    });
  }

  void _onCancelTap() {
    Navigator.pop(context);
  }

  void _onSubmitTap() {
    Navigator.pop(context, _selectedValues);
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Select items'),
      contentPadding: const EdgeInsets.only(top: 12.0),
      content: SingleChildScrollView(
        child: ListTileTheme(
          contentPadding: const EdgeInsets.fromLTRB(14.0, 0.0, 24.0, 0.0),
          child: ListBody(
            children: widget.items.map(_buildItem).toList(),
          ),
        ),
      ),
      actions: <Widget>[
        ElevatedButton(
          child: const Text('Cancel'),
          onPressed: _onCancelTap,
        ),
        ElevatedButton(
          child: const Text('Ok'),
          onPressed: _onSubmitTap,
        )
      ],
    );
  }

  Widget _buildItem(MultiSelectDialogItem<V> item) {
    final checked = _selectedValues.contains(item.value);
    return CheckboxListTile(
      value: checked,
      title: Text(item.label),
      controlAffinity: ListTileControlAffinity.leading,
      onChanged: (checked) =>
          _onItemCheckedChange(item.value, checked ?? false),
    );
  }
}
Другие вопросы по тегам