По умолчанию ListView не поддерживает отслеживание выделенного элемента, и в такой ситуации нам надо создавать самим всю инфраструктуру обработки выделения элемента. В этом случае мы можем воспользоваться ранее рассмотренным виджетом GestureDetector, который позволяет отслеживать нажатия.
Например, определим следующий код:
import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( home: Scaffold( body: UsersList(), appBar: AppBar(title: Text("METANIT.COM")),) )); } class UsersList extends StatefulWidget { @override _UsersListState createState() => _UsersListState(); } class _UsersListState extends State<UsersList> { final List<String> users = ["Tom", "Alice", "Sam", "Bob", "Kate"]; int selectedIndex = -1; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( selectedIndex==-1?"Не выбрано": "Выбрано: ${users[selectedIndex]}", style: TextStyle(fontSize: 30)), Expanded(child: ListView.builder( itemCount: users.length, itemBuilder: _createListView, )) ]); } Widget _createListView(BuildContext context, int index) { return GestureDetector( onTap: () { setState(() { // устанавливаем индекс выделенного элемента selectedIndex = index; }); }, child: Container( margin: EdgeInsets.symmetric(vertical: 4), padding: EdgeInsets.symmetric(vertical: 8), color: index == selectedIndex ? Colors.black12: Colors.white60, child: Text(users[index], style: TextStyle(fontSize: 24)), ), ); } }
Итак, в данном случае определен новый виджет - UsersList, который может хранить состояние. Все его состояние инкапсулировано
в рамках класса _UsersListState
_UsersListState хранит список users
- собственно данные, которые будут выводиться в ListView:
final List<String> users = ["Tom", "Alice", "Sam", "Bob", "Kate"];
Для отслеживания выделенного элемента нам потребуется его индекс. И для хранения индекса выделенного элемента из списка users определим отдельную переменную:
int selectedIndex = -1;
По умолчанию она равна -1, что будет выступать в качестве индикатора, что ни одного элемента еще не выбрано. После выделения элемента в ListView эта переменная будет хранить индекс выделенного элемента.
Организационно все виджеты будут помещаться в контейнер Column
, в котором первый виджет - Text будет
выводить значение выделенного элемента (если такой имеется):
Text( selectedIndex==-1?"Не выбрано": "Выбрано: ${users[selectedIndex]}", style: TextStyle(fontSize: 30)),
Чтобы у ListView не было проблем с заполнением пространства контейнера помещаем этот виджет в контейнер Expander.
А само наполнение ListView для наглядности вынесено в отдельный метод _createListView
. напомню, что в конструкторе ListView
параметр itemBuilder принимает функцию, которая в качестве второго параметра получает индекс элемента:
Widget _createListView(BuildContext context, int index) {
Так из параметра метода _createListView мы можем получить индекс текущего элемента и что-то с ним сделать.
Контейнером верхнего уровня для элемента в ListView служит виджет GestureDetector, который позволяет обрабатывать нажатия.
При нажатии будет срабатывать функция, которая передается через параметр onTap
виджета GestureDetector:
onTap: () { setState(() { // устанавливаем индекс выделенного элемента selectedIndex = index; }); },
Здесь мы изменяем состояние виджета, передавая в переменную selectedIndex
индекс нажатого элемента.
Сам GestureDetector содержит виджет Container
, а тот - виджет Text
, который выводит строку
из списка users
.
Чтобы выделить отмеченный элемент, в виджете Container фоновый цвет устанавливается в зависимости от того, является ли текущий индекс индексом выделенного элемента:
color: index == selectedIndex ? Colors.black12: Colors.white60,
В итоге мы сможем выделять элементы в ListView и получать их: