Как передать переменные как состояние в методах виджета? - PullRequest
0 голосов
/ 09 июля 2020

У меня есть фрагмент кода, который отображает DropdownButton во Flutter. Мне понадобилось несколько раскрывающихся списков, поэтому я извлек их в метод, вызываемый для каждого нужного мне экземпляра. В нем я передаю переменные состояния для выбранного значения, а также все параметры. Внутри DropdownButton у меня есть методы для обновления selected в методе DropdownButton onChanged.

Таким образом, проблема возникает, когда я вызываю метод вместо того, чтобы использовать его на месте, как функции для onChanged и onClear не перестраивают виджет, потому что они не обновляют переменные состояния вне вызова метода раскрывающегося списка. Как лучше всего это сделать? Это сработает, если я передам функции извне, но тогда мне придется продублировать некоторый код. Следует ли мне переписать его как StatefulWidget?

DropdownButton myDropdown(
  String selected,
  List<dynamic> options, {
  Function onChanged,
  Function onClear,
}) {
  return DropdownButton<String>(
    value: selected,
    // onChanged: (String newValue) { // Not working
    //   setState(() {
    //     selected = newValue;
    //   });
    // },
    onChanged: onChanged,
    selectedItemBuilder: (BuildContext context) {
      return options?.map<Widget>((dynamic item) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            Text(item),
            IconButton(
              icon: Icon(Icons.clear),
              // onPressed: () { // Not working
              //   setState(() {
              //     selected = null;
              //   });
              // },
              onPressed: onClear,
            ),
          ],
        );
      })?.toList() ?? [];
    },
    items: options?.map<DropdownMenuItem<String>>((dynamic value) {
      return DropdownMenuItem<String>(
        value: value,
        child: Text(value),
      );
    })?.toList() ?? [],
  );
}

(В проекте я использую провайдера для управления состоянием, но я не уверен, действительно ли это актуально для этого вопроса.)

Ответы [ 2 ]

1 голос
/ 09 июля 2020

Решение состоит в том, чтобы управлять состоянием значения DropdownButton, заключая его в StatefulWidget.

Я привел пример настраиваемого Widget, который выполняет это, в в духе исходного кода.


import 'package:flutter/material.dart';

/// A managed [DropdownButton] initialized to [selected] with possible values
/// [options].
///
/// Selected value is updated on dropdown item clicks, and triggers [onChanged]
/// callback.
///
/// An "x" clear button is present on all menu items, and sets selected item to
/// null, while also triggering [onClear] callback.
class SuperDropdown extends StatefulWidget {
  SuperDropdown({
    @required this.selected,
    @required this.options,
    this.onClear,
    this.onChanged,
  });
  final Function(String s) onChanged;
  final Function() onClear;
  final List<String> options;
  final String selected;

  @override
  _SuperDropdownState createState() => _SuperDropdownState();
}

class _SuperDropdownState extends State<SuperDropdown> {
  String selected;

  List<DropdownMenuItem> get items => widget.options
      .map((i) => DropdownMenuItem(
            value: i,
            child: Text(i),
          ))
      .toList();

  void onChanged(String value) {
    setState(() => selected = value);
    widget.onChanged?.call(value);
  }

  void onClear() {
    setState(() => selected = null);
    widget.onClear?.call();
  }

  List<Widget> itemBuilder(BuildContext context) => widget.options
      .map((i) =>
          Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
            Text(i),
            IconButton(
              icon: Icon(Icons.clear),
              onPressed: onClear,
            ),
          ]))
      .toList();
  @override
  void initState() {
    super.initState();
    selected = widget.selected;
  }

  @override
  Widget build(BuildContext context) => DropdownButton<String>(
        value: selected,
        items: items,
        onChanged: onChanged,
        selectedItemBuilder: itemBuilder,
      );
}

void main() => runApp(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: Column(
              children: ["abc", "def"]
                  .map((letters) => SuperDropdown(
                        selected: letters.substring(0, 1),
                        options: letters.split(''),
                        onChanged: (s) =>
                            print("dropdown $letters changed to $s"),
                        onClear: () => print("dropdown $letters cleared"),
                      ))
                  .toList(),
            ),
          ),
        ),
      ),
    );
0 голосов
/ 09 июля 2020

Определите selected где-нибудь в родительском классе, откуда вы вызываете DropdownButton

var selected;

Теперь напишите DropdownButton следующим образом -

DropdownButton(
 selected,
 [whatever],
 onChanged: (newValue) {
    selected = newValue;
    setState(() {});
 } 
 onClear: () {
  selected = null;
  setState(() {}); 
}
)

Итак, как только onChanged или onClear будет вызван, он изменит значение selected и обновит sh виджет с новым выбранным значением.

...