Line data Source code
1 : import 'package:flutter/material.dart';
2 :
3 : import '../navigation_controller.dart';
4 : import 'index.dart';
5 :
6 : /// A [NavigatorBuilder] that allows to switch between destinations using
7 : /// [Drawer].
8 : ///
9 : /// It builds a wrapper widget, which is a [Scaffold] with a [Scaffold.body] set
10 : /// to the current destination's content, and [Scaffold.drawer] specified.
11 : ///
12 : /// The [drawerItems] must correspond to the navigator's destinations.
13 : ///
14 : /// The drawer can be customized using [parameters], which includes
15 : /// all parameters supported by the [Drawer] widget.
16 : ///
17 : /// See also:
18 : /// - [NavigatorBuilder]
19 : /// - [DrawerParameters]
20 : /// - [NavigationController]
21 : /// - [Drawer]
22 : ///
23 : class DrawerNavigationBuilder extends NavigatorBuilder {
24 : /// Creates a [DrawerNavigationBuilder] instance.
25 : ///
26 0 : const DrawerNavigationBuilder({
27 : required this.drawerItems,
28 : this.header,
29 : this.parameters,
30 0 : }) : super();
31 :
32 : /// A list of [DrawerItems], that corresponds to the navigator's
33 : /// destination list.
34 : ///
35 : /// The list must contain the same number of drawer items,
36 : /// following with the same order as a destination list specified for the navigator.
37 : ///
38 : final List<DrawerItem> drawerItems;
39 :
40 : /// Optional header, that is shown at the top of drawer.
41 : ///
42 : final Widget? header;
43 :
44 : /// A set of [Drawer`] parameters.
45 : ///
46 : /// Contains all supported parameters to customize [Drawer] widget.
47 : /// Doesn't include 'child', which is managed by [DrawerNavigationBuilder].
48 : ///
49 : final DrawerParameters? parameters;
50 :
51 0 : @override
52 : Widget build(BuildContext context, NavigationController navigator) {
53 0 : final currentDestination = navigator.currentDestination;
54 0 : final content = currentDestination.build(context);
55 0 : return _DrawerWrapper(
56 : content: content,
57 0 : items: drawerItems,
58 0 : header: header,
59 0 : parameters: parameters,
60 0 : onSelectItem: (index) => navigator.goTo(navigator.destinations[index]),
61 0 : selectedIndex: navigator.destinations.indexOf(currentDestination),
62 : );
63 : }
64 : }
65 :
66 : class _DrawerWrapper extends StatefulWidget {
67 0 : const _DrawerWrapper({
68 : Key? key,
69 : required this.content,
70 : required this.items,
71 : required this.onSelectItem,
72 : required this.selectedIndex,
73 : this.header,
74 : this.parameters,
75 0 : }) : super(key: key);
76 :
77 : final Widget content;
78 :
79 : final List<DrawerItem> items;
80 :
81 : final void Function(int) onSelectItem;
82 :
83 : final int selectedIndex;
84 :
85 : final Widget? header;
86 :
87 : final DrawerParameters? parameters;
88 :
89 0 : @override
90 0 : _DrawerWrapperState createState() => _DrawerWrapperState();
91 : }
92 :
93 : class _DrawerWrapperState extends State<_DrawerWrapper> {
94 : late final OverlayEntry _mainOverlay;
95 :
96 0 : @override
97 : void initState() {
98 0 : super.initState();
99 0 : _mainOverlay = OverlayEntry(
100 0 : builder: (context) => Row(
101 0 : children: [
102 0 : Drawer(
103 0 : backgroundColor: widget.parameters?.backgroundColor,
104 0 : elevation: widget.parameters?.elevation,
105 0 : shape: widget.parameters?.shape,
106 0 : width: widget.parameters?.width,
107 0 : semanticLabel: widget.parameters?.semanticLabel,
108 0 : child: ListView(
109 : padding: EdgeInsets.zero,
110 0 : children: [
111 0 : if (widget.header != null) widget.header!,
112 0 : ...widget.items
113 0 : .map((item) => ListTile(
114 0 : leading: item.leading,
115 0 : title: Text(item.title ?? ''),
116 0 : selected: widget.selectedIndex ==
117 0 : widget.items.indexOf(item),
118 0 : selectedColor: widget.parameters?.selectedColor,
119 : selectedTileColor:
120 0 : widget.parameters?.selectedTileColor,
121 0 : onTap: () =>
122 0 : widget.onSelectItem(widget.items.indexOf(item)),
123 : ))
124 0 : .toList(),
125 : ],
126 : )),
127 0 : Expanded(
128 0 : child: Scaffold(
129 0 : body: widget.content,
130 : ),
131 : ),
132 : ],
133 : ),
134 : );
135 : }
136 :
137 0 : @override
138 : void didUpdateWidget(_DrawerWrapper oldWidget) {
139 0 : super.didUpdateWidget(oldWidget);
140 0 : if (oldWidget.selectedIndex != widget.selectedIndex) {
141 0 : _mainOverlay.markNeedsBuild();
142 : }
143 : }
144 :
145 0 : @override
146 : Widget build(BuildContext context) {
147 0 : return Overlay(
148 0 : initialEntries: [
149 0 : _mainOverlay,
150 : ],
151 : );
152 : }
153 : }
154 :
155 : /// A model of drawer item.
156 : ///
157 : /// The data provided in the model is used to create a [ListTile] widget, which
158 : /// represents the drawer list item.
159 : ///
160 : class DrawerItem {
161 : /// Creates an instance of [DrawerItem].
162 : ///
163 0 : const DrawerItem({
164 : this.leading,
165 : this.title,
166 : });
167 :
168 : /// A leading widget in the drawer item.
169 : ///
170 : /// Usually it is an icon, but could be any widget.
171 : ///
172 : final Widget? leading;
173 :
174 : /// An item's title.
175 : ///
176 : final String? title;
177 : }
178 :
179 : /// Contains parameters to customize the [Drawer].
180 : ///
181 : /// It includes all the same arguments as the [Drawer()], excepting
182 : /// the 'child', which is managed by the [DrawerNavigationBuilder].
183 : ///
184 : /// In addition it includes some parameters of [ListTile()] that are used to style
185 : /// selected drawer item.
186 : ///
187 : /// See also:
188 : /// - [DrawerNavigationBuilder]
189 : /// - [Drawer]
190 : /// - [ListTile]
191 : ///
192 : class DrawerParameters {
193 : /// Create a [DrawerParameters] instance.
194 : ///
195 0 : const DrawerParameters({
196 : this.backgroundColor,
197 : this.elevation,
198 : this.shape,
199 : this.width,
200 : this.semanticLabel,
201 : this.selectedColor,
202 : this.selectedTileColor,
203 : });
204 :
205 : /// [Drawer.backgroundColor]
206 : ///
207 : final Color? backgroundColor;
208 :
209 : /// [Drawer.elevation]
210 : ///
211 : final double? elevation;
212 :
213 : /// [Drawer.shape]
214 : ///
215 : final ShapeBorder? shape;
216 :
217 : /// [Drawer.width]
218 : ///
219 : final double? width;
220 :
221 : /// [Drawer.semanticLabel]
222 : ///
223 : final String? semanticLabel;
224 :
225 : /// [ListTile.selectedColor]
226 : ///
227 : final Color? selectedColor;
228 :
229 : /// [ListTile.selectedTileColor]
230 : ///
231 : final Color? selectedTileColor;
232 : }
|