| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /// Flutter code sample for [ListTile] selection in a ListView or GridView. |
| // Long press any ListTile to enable selection mode. |
| |
| import 'package:flutter/material.dart'; |
| |
| void main() => runApp(const MyApp()); |
| |
| class MyApp extends StatelessWidget { |
| const MyApp({super.key}); |
| |
| static const String _title = 'Flutter Code Sample'; |
| |
| @override |
| Widget build(BuildContext context) { |
| return const MaterialApp( |
| title: _title, |
| home: ListTileSelectExample(), |
| ); |
| } |
| } |
| |
| class ListTileSelectExample extends StatefulWidget { |
| const ListTileSelectExample({super.key}); |
| |
| @override |
| ListTileSelectExampleState createState() => ListTileSelectExampleState(); |
| } |
| |
| class ListTileSelectExampleState extends State<ListTileSelectExample> { |
| bool isSelectionMode = false; |
| final int listLength = 30; |
| late List<bool> _selected; |
| bool _selectAll = false; |
| bool _isGridMode = false; |
| |
| @override |
| void initState() { |
| super.initState(); |
| initializeSelection(); |
| } |
| |
| void initializeSelection() { |
| _selected = List<bool>.generate(listLength, (_) => false); |
| } |
| |
| @override |
| void dispose() { |
| _selected.clear(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Scaffold( |
| appBar: AppBar( |
| title: const Text( |
| 'ListTile selection', |
| ), |
| leading: isSelectionMode |
| ? IconButton( |
| icon: const Icon(Icons.close), |
| onPressed: () { |
| setState(() { |
| isSelectionMode = false; |
| }); |
| initializeSelection(); |
| }, |
| ) |
| : const SizedBox(), |
| actions: <Widget>[ |
| if (_isGridMode) |
| IconButton( |
| icon: const Icon(Icons.grid_on), |
| onPressed: () { |
| setState(() { |
| _isGridMode = false; |
| }); |
| }, |
| ) |
| else |
| IconButton( |
| icon: const Icon(Icons.list), |
| onPressed: () { |
| setState(() { |
| _isGridMode = true; |
| }); |
| }, |
| ), |
| if (isSelectionMode) |
| TextButton( |
| child: !_selectAll |
| ? const Text( |
| 'select all', |
| style: TextStyle(color: Colors.white), |
| ) |
| : const Text( |
| 'unselect all', |
| style: TextStyle(color: Colors.white), |
| ), |
| onPressed: () { |
| _selectAll = !_selectAll; |
| setState(() { |
| _selected = |
| List<bool>.generate(listLength, (_) => _selectAll); |
| }); |
| }), |
| ], |
| ), |
| body: _isGridMode |
| ? GridBuilder( |
| isSelectionMode: isSelectionMode, |
| selectedList: _selected, |
| onSelectionChange: (bool x) { |
| setState(() { |
| isSelectionMode = x; |
| }); |
| }, |
| ) |
| : ListBuilder( |
| isSelectionMode: isSelectionMode, |
| selectedList: _selected, |
| onSelectionChange: (bool x) { |
| setState(() { |
| isSelectionMode = x; |
| }); |
| }, |
| )); |
| } |
| } |
| |
| class GridBuilder extends StatefulWidget { |
| const GridBuilder({ |
| super.key, |
| required this.selectedList, |
| required this.isSelectionMode, |
| required this.onSelectionChange, |
| }); |
| |
| final bool isSelectionMode; |
| final Function(bool)? onSelectionChange; |
| final List<bool> selectedList; |
| |
| @override |
| GridBuilderState createState() => GridBuilderState(); |
| } |
| |
| class GridBuilderState extends State<GridBuilder> { |
| void _toggle(int index) { |
| if (widget.isSelectionMode) { |
| setState(() { |
| widget.selectedList[index] = !widget.selectedList[index]; |
| }); |
| } |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return GridView.builder( |
| itemCount: widget.selectedList.length, |
| gridDelegate: |
| const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), |
| itemBuilder: (_, int index) { |
| return InkWell( |
| onTap: () => _toggle(index), |
| onLongPress: () { |
| if (!widget.isSelectionMode) { |
| setState(() { |
| widget.selectedList[index] = true; |
| }); |
| widget.onSelectionChange!(true); |
| } |
| }, |
| child: GridTile( |
| child: Container( |
| child: widget.isSelectionMode |
| ? Checkbox( |
| onChanged: (bool? x) => _toggle(index), |
| value: widget.selectedList[index]) |
| : const Icon(Icons.image), |
| )), |
| ); |
| }); |
| } |
| } |
| |
| class ListBuilder extends StatefulWidget { |
| const ListBuilder({ |
| super.key, |
| required this.selectedList, |
| required this.isSelectionMode, |
| required this.onSelectionChange, |
| }); |
| |
| final bool isSelectionMode; |
| final List<bool> selectedList; |
| final Function(bool)? onSelectionChange; |
| |
| @override |
| State<ListBuilder> createState() => _ListBuilderState(); |
| } |
| |
| class _ListBuilderState extends State<ListBuilder> { |
| void _toggle(int index) { |
| if (widget.isSelectionMode) { |
| setState(() { |
| widget.selectedList[index] = !widget.selectedList[index]; |
| }); |
| } |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return ListView.builder( |
| itemCount: widget.selectedList.length, |
| itemBuilder: (_, int index) { |
| return ListTile( |
| onTap: () => _toggle(index), |
| onLongPress: () { |
| if (!widget.isSelectionMode) { |
| setState(() { |
| widget.selectedList[index] = true; |
| }); |
| widget.onSelectionChange!(true); |
| } |
| }, |
| trailing: widget.isSelectionMode |
| ? Checkbox( |
| value: widget.selectedList[index], |
| onChanged: (bool? x) => _toggle(index), |
| ) |
| : const SizedBox.shrink(), |
| title: Text('item $index')); |
| }); |
| } |
| } |