Отправка обновленного состояния в флаттер дочернего виджета - PullRequest
0 голосов
/ 08 марта 2020

У меня есть родительский виджет, который содержит список элементов и индекс выбранного элемента. Дочерний виджет сообщает родителю, когда элемент выбран. Если я передам функцию isSelected (index) дочернему виджету, дочерний виджет обновится, чтобы показать, какой элемент выбран. Если я передаю selectedIndex int дочернему виджету, selectedIndex обновляется в родительском, но не дочернем виджете.

Рабочая версия с использованием функции isSelected (index)

import 'package:flutter/material.dart';

class IsSelected extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => IsSelectedState();
}

class IsSelectedState extends State<IsSelected> {
  List<int> _items = List.from([0]);
  int _selectedIndex = -1;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.indeterminate_check_box),
                onPressed: () {
                  setState(() {
                    _items.removeAt(_selectedIndex);
                  });
                }
              ),
              IconButton(
                icon: Icon(Icons.add_box),
                onPressed: () {
                  setState(() {
                    _items.add(_items.length);
                  });
                }
              )
            ]
          ),
          Expanded(
            child: ItemList(
              items: _items,
              isSelected: (index) => _selectedIndex == index,
              onSelect: (index) => setState(() {
                _selectedIndex = index;
              })
            )
          )
        ],
      )
    );
  }
}

class ItemList extends StatefulWidget {
  final List<int> _items;
  final Function _isSelected;
  final Function _onSelect;

  ItemList({items, isSelected, onSelect}) :
    _items = items,
    _isSelected = isSelected,
    _onSelect = onSelect;

  @override
  State<StatefulWidget> createState() => ItemListState(
    items: _items,
    isSelected: _isSelected,
    onSelect: _onSelect
  );
}

class ItemListState extends State<ItemList> {
  final List<int> _items;
  final Function _isSelected;
  final Function _onSelect;


  ItemListState({items, isSelected, onSelect}) :
    _items = items,
    _isSelected = isSelected,
    _onSelect = onSelect;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _items.length,
      itemBuilder: (BuildContext context, int index) {
        // I used a withOpacity(0) below to make the border transparent
        // The background colors of the ListView isn't quite white
        final Color borderColor = _isSelected(index) ?
          Theme.of(context).primaryColor : Colors.white.withOpacity(0);
        return GestureDetector(
          onTap: () => _onSelect(index),
          child: Container(
            padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
            decoration: BoxDecoration(
              border: Border.all(
                color: borderColor,
                width: 2
              )
            ),
            child: Row(
              children: <Widget> [
                Expanded(
                  child: Text(_items[index].toString(),
                    textAlign: TextAlign.left,
                    overflow: TextOverflow.ellipsis,
                  )
                )
              ]
            )
          )
        );
      },
    );
  }
}

Не работает версия, передавая selectedIndex дочернему виджету

import 'package:flutter/material.dart';

class SelectedIndex extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => SelectedIndexState();
}

class SelectedIndexState extends State<SelectedIndex> {
  List<int> _items = List.from([0]);
  int _selectedIndex = -1;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.indeterminate_check_box),
                onPressed: () {
                  setState(() {
                    _items.removeAt(_selectedIndex);
                  });
                }
              ),
              IconButton(
                icon: Icon(Icons.add_box),
                onPressed: () {
                  setState(() {
                    _items.add(_items.length);
                  });
                }
              )
            ]
          ),
          Expanded(
            child: ItemList(
              items: _items,
              selectedIndex: _selectedIndex,
              onSelect: (index) => setState(() {
                _selectedIndex = index;
              })
            )
          )
        ],
      )
    );
  }
}

class ItemList extends StatefulWidget {
  final List<int> _items;
  final int _selectedIndex;
  final Function _onSelect;

  ItemList({items, selectedIndex, onSelect}) :
    _items = items,
    _selectedIndex = selectedIndex,
    _onSelect = onSelect;

  @override
  State<StatefulWidget> createState() => ItemListState(
    items: _items,
    selectedIndex: _selectedIndex,
    onSelect: _onSelect
  );
}

class ItemListState extends State<ItemList> {
  final List<int> _items;
  final int _selectedIndex;
  final Function _onSelect;


  ItemListState({items, selectedIndex, onSelect}) :
    _items = items,
    _selectedIndex = selectedIndex,
    _onSelect = onSelect;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _items.length,
      itemBuilder: (BuildContext context, int index) {
        // I used a withOpacity(0) below to make the border transparent
        // The background colors of the ListView isn't quite white
        final Color borderColor = _selectedIndex == index ?
          Theme.of(context).primaryColor : Colors.white.withOpacity(0);
        return GestureDetector(
          onTap: () => _onSelect(index),
          child: Container(
            padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
            decoration: BoxDecoration(
              border: Border.all(
                color: borderColor,
                width: 2
              )
            ),
            child: Row(
              children: <Widget> [
                Expanded(
                  child: Text(_items[index].toString(),
                    textAlign: TextAlign.left,
                    overflow: TextOverflow.ellipsis,
                  )
                )
              ]
            )
          )
        );
      },
    );
  }
}

Я понимаю, что в первой версии функция, которую я передаю дочернему виджету, никогда не изменяется. Вот почему у меня нет проблем с отображением выбранного элемента. Может ли кто-нибудь помочь мне понять, чего мне не хватает во втором примере. Почему флаттер не обновляет selectedIndex в дочернем виджете? Используя отладчик, я вижу, что дочерний виджет перестраивается при обновлении selectedIndex, но значение в дочернем виджете остается прежним. Я родом из React, где все, что мне нужно сделать, чтобы обновить ребенка, это обновить реквизит. Разве это не так, как работает Flutter?

...