jpnurmi / item_selector
1
// MIT License
2
//
3
// Copyright (c) 2020 J-P Nurmi
4
//
5
// The ItemSelector library is based on:
6
// Multi Select GridView in Flutter - by Simon Lightfoot:
7
// https://gist.github.com/slightfoot/a002dd1e031f5f012f810c6d5da14a11
8
//
9
// Copyright (c) 2019 Simon Lightfoot
10
//
11
// Permission is hereby granted, free of charge, to any person obtaining a copy
12
// of this software and associated documentation files (the "Software"), to deal
13
// in the Software without restriction, including without limitation the rights
14
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
// copies of the Software, and to permit persons to whom the Software is
16
// furnished to do so, subject to the following conditions:
17
//
18
// The above copyright notice and this permission notice shall be included in all
19
// copies or substantial portions of the Software.
20
//
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
// SOFTWARE.
28
//
29
// Thanks to Hugo Passos.
30
//
31
import 'package:flutter/widgets.dart';
32

33
import 'item_selection_controller.dart';
34
import 'item_selection_types.dart';
35

36
/// Builds itself based on selection state changes.
37
///
38
/// ItemSelectionBuilder is used in conjunction with [ItemSelectionController].
39
/// Widget rebuilding is scheduled by appropriate selection changes in the
40
/// controller.
41
///
42
/// Notice that you must tell ItemSelectionBuilder the [index] it represents,
43
/// and a [builder] method that is called when the selection state of that
44
/// particular index changes.
45
///
46
/// ### Example
47
///
48
///     Widget build(BuildContext context) {
49
///       return ItemSelectionController(
50
///         child: Column(
51
///           children: List.generate(5, (int index) {
52
///             // tell the builder which index it represents
53
///             return ItemSelectionBuilder(
54
///               index: index,
55
///               builder: (BuildContext context, int index, bool selected) {
56
///                 // build a selectable item
57
///                 return Text('$index: $selected');
58
///               },
59
///             );
60
///           }),
61
///         ),
62
///       );
63
///     }
64
class ItemSelectionBuilder extends StatefulWidget {
65
  /// Creates an item selection builder.
66
  ///
67
  /// Notice that you must tell ItemSelectionBuilder the [index] it represents,
68
  /// and a [builder] method that is called when the selection state of that
69
  /// particular index changes.
70 1
  ItemSelectionBuilder({
71
    Key? key,
72
    required this.index,
73
    ItemSelectionController? controller,
74
    required ItemSelectionWidgetBuilder builder,
75 1
  })  : assert(index != -1),
76
        _controller = controller,
77
        _builder = builder,
78 1
        super(key: key ?? ValueKey(index));
79

80
  /// The index of this builder widget.
81
  final int index;
82

83 1
  @override
84 1
  _ItemSelectionBuilderState createState() => _ItemSelectionBuilderState();
85

86
  final ItemSelectionWidgetBuilder _builder;
87
  final ItemSelectionController? _controller;
88
}
89

90
class _ItemSelectionBuilderState extends State<ItemSelectionBuilder> {
91
  bool _selected = false;
92
  ItemSelectionController? _controller;
93

94 1
  @override
95
  Widget build(BuildContext context) {
96 1
    return MetaData(
97 1
      metaData: ItemSelectionMetaData(index: widget.index),
98 1
      child: widget._builder(context, widget.index, _selected),
99
    );
100
  }
101

102 1
  @override
103
  void dispose() {
104 1
    if (_controller != null) {
105 1
      _controller!.selection
106 1
          .removeIndexListener(widget.index, _updateSelection);
107
    }
108 1
    _controller = null;
109 1
    super.dispose();
110
  }
111

112 1
  @override
113
  void didChangeDependencies() {
114 1
    super.didChangeDependencies();
115 1
    _updateController();
116
  }
117

118 0
  @override
119
  void didUpdateWidget(ItemSelectionBuilder oldWidget) {
120 0
    super.didUpdateWidget(oldWidget);
121 0
    if (widget._controller != oldWidget._controller) _updateController();
122
  }
123

124 1
  void _updateController() {
125
    final newController =
126 1
        widget._controller ?? ItemSelectionController.of(context);
127 1
    assert(() {
128
      if (newController == null) {
129 1
        throw FlutterError(
130 1
            'No ItemSelectionController for ${widget.runtimeType}.\n'
131 1
            'When creating a ${widget.runtimeType}, you must either provide an explicit '
132
            'ItemSelectionController using the "controller" property, or you must ensure that there '
133 1
            'is a ItemSelectionController above the ${widget.runtimeType}.\n'
134
            'In this case, there was neither an explicit controller nor a default controller.');
135
      }
136
      return true;
137 1
    }());
138

139 1
    if (newController == _controller) return;
140

141 1
    final index = widget.index;
142 1
    if (_controller != null) {
143 0
      _controller!.selection.removeIndexListener(index, _updateSelection);
144
    }
145 1
    _controller = newController;
146 1
    if (_controller != null) {
147 1
      _controller!.selection.addIndexListener(index, _updateSelection);
148 1
      _updateSelection(index, _controller!.selection.contains(index));
149
    }
150
  }
151

152 1
  void _updateSelection(int index, bool selected) {
153 1
    assert(index == widget.index);
154 1
    if (_selected != selected) {
155 1
      setState(() {
156 1
        _selected = selected;
157
      });
158
    }
159
  }
160
}

Read our documentation on viewing source code .

Loading