1- import 'package:dpip/app/home/_models/home_location.dart' ;
21import 'package:dpip/api/model/location/location.dart' ;
3- import 'package:dpip/app/home/_widgets/blurred_button .dart' ;
2+ import 'package:dpip/app/home/_models/home_red_button .dart' ;
43import 'package:dpip/core/i18n.dart' ;
54import 'package:dpip/global.dart' ;
65import 'package:dpip/models/settings/location.dart' ;
76import 'package:dpip/router.dart' ;
87import 'package:dpip/utils/extensions/build_context.dart' ;
9- import 'package:dpip/widgets/list/segmented_list.dart' ;
108import 'package:flutter/material.dart' ;
119import 'package:material_symbols_icons/symbols.dart' ;
1210import 'package:provider/provider.dart' ;
1311
1412class LocationButton extends StatelessWidget {
15- const LocationButton ({super .key});
13+ final String ? temporaryCode;
14+ final ValueChanged <String ?>? onLocationChanged;
15+
16+ const LocationButton ({
17+ super .key,
18+ this .temporaryCode,
19+ this .onLocationChanged,
20+ });
1621
1722 @override
1823 Widget build (BuildContext context) {
19- return Consumer2 <HomeLocationModel , SettingsLocationModel >(
20- builder: (context, homeLocation, settingsLocation, child) {
21- final savedCode = settingsLocation.code;
22- final favorited = settingsLocation.favorited;
23- final temporaryCode = homeLocation.temporaryCode;
24+ return Selector <SettingsLocationModel , (String ?, Set <String >)>(
25+ selector: (context, model) => (model.code, model.favorited),
26+ builder: (context, data, child) {
27+ final (savedCode, favorited) = data;
2428 final displayCode = temporaryCode ?? savedCode;
2529 final location = Global .location[displayCode];
2630
@@ -56,27 +60,22 @@ class LocationButton extends StatelessWidget {
5660
5761 showModalBottomSheet <String ?>(
5862 context: context,
59- constraints : context.bottomSheetConstraints ,
63+ backgroundColor : Colors .transparent ,
6064 isScrollControlled: true ,
61- builder: (context ) => _LocationMenuSheet (
65+ builder: (sheetContext ) => _LocationMenuSheet (
6266 savedCode: savedCode,
6367 favorited: favorited,
6468 currentCode: currentCode,
6569 onLocationSelected: (code) {
66- Navigator .of (context).pop ();
67- final model = context.read <HomeLocationModel >();
70+ Navigator .of (sheetContext).pop ();
6871 if (code == savedCode) {
69- model. setTemporaryCode (null );
72+ onLocationChanged ? . call (null );
7073 } else {
71- model. setTemporaryCode (code);
74+ onLocationChanged ? . call (code);
7275 }
7376 },
74- onAddLocationPressed: () {
75- Navigator .of (context).pop ();
76- SettingsLocationSelectRoute ().push (context);
77- },
7877 onSettingsPressed: () {
79- Navigator .of (context ).pop ();
78+ Navigator .of (sheetContext ).pop ();
8079 SettingsLocationRoute ().push (context);
8180 },
8281 ),
@@ -89,15 +88,13 @@ class _LocationMenuSheet extends StatefulWidget {
8988 final Set <String > favorited;
9089 final String ? currentCode;
9190 final ValueChanged <String > onLocationSelected;
92- final VoidCallback onAddLocationPressed;
9391 final VoidCallback onSettingsPressed;
9492
9593 const _LocationMenuSheet ({
9694 required this .savedCode,
9795 required this .favorited,
9896 required this .currentCode,
9997 required this .onLocationSelected,
100- required this .onAddLocationPressed,
10198 required this .onSettingsPressed,
10299 });
103100
@@ -125,32 +122,57 @@ class _LocationMenuSheetState extends State<_LocationMenuSheet> {
125122
126123 @override
127124 Widget build (BuildContext context) {
128- return Material (
129- borderRadius: const .vertical (top: .circular (16 )),
130- clipBehavior: .antiAlias,
131- child: Scaffold (
132- appBar: AppBar (
133- leading: BackButton (
134- onPressed: _selectedCity != null
135- ? () => setState (() => _selectedCity = null )
136- : null ,
125+ return Container (
126+ margin: const EdgeInsets .all (16 ),
127+ constraints: BoxConstraints (
128+ maxHeight: MediaQuery .of (context).size.height * 0.7 ,
129+ ),
130+ decoration: BoxDecoration (
131+ color: context.colors.surfaceContainer,
132+ borderRadius: BorderRadius .circular (16 ),
133+ ),
134+ child: Column (
135+ mainAxisSize: MainAxisSize .min,
136+ children: [
137+ _buildHeader (context),
138+ Flexible (
139+ child: _selectedCity == null
140+ ? _buildCityList (context)
141+ : _buildTownList (context),
137142 ),
138- title: Text ('切換區域' .i18n),
139- centerTitle: true ,
140- actions: [
143+ ],
144+ ),
145+ );
146+ }
147+
148+ Widget _buildHeader (BuildContext context) {
149+ return Padding (
150+ padding: const EdgeInsets .fromLTRB (8 , 8 , 8 , 0 ),
151+ child: Row (
152+ children: [
153+ if (_selectedCity != null )
141154 IconButton (
142- icon: const Icon (Symbols .settings_rounded),
143- onPressed: widget.onSettingsPressed,
144- tooltip: '位置設定' .i18n,
155+ onPressed: () => setState (() => _selectedCity = null ),
156+ icon: const Icon (Symbols .arrow_back_rounded, size: 20 ),
157+ tooltip: '返回' .i18n,
158+ )
159+ else
160+ const SizedBox (width: 48 ),
161+ Expanded (
162+ child: Text (
163+ _selectedCity ?? '切換區域' .i18n,
164+ style: context.theme.textTheme.titleMedium? .copyWith (
165+ fontWeight: FontWeight .w600,
166+ ),
167+ textAlign: TextAlign .center,
145168 ),
146- ],
147- ),
148- body: SingleChildScrollView (
149- padding: .only (bottom: context.padding.bottom + 16 ),
150- child: _selectedCity == null
151- ? _buildCityList (context)
152- : _buildTownList (context),
153- ),
169+ ),
170+ IconButton (
171+ onPressed: widget.onSettingsPressed,
172+ icon: const Icon (Symbols .settings_rounded, size: 20 ),
173+ tooltip: '位置設定' .i18n,
174+ ),
175+ ],
154176 ),
155177 );
156178 }
@@ -188,92 +210,111 @@ class _LocationMenuSheetState extends State<_LocationMenuSheet> {
188210 }
189211 }
190212
191- return Column (
192- crossAxisAlignment: .start,
213+ return ListView (
214+ shrinkWrap: true ,
215+ padding: const EdgeInsets .only (bottom: 16 ),
193216 children: [
194- SegmentedList (
195- label: Text ('快速切換' .i18n),
196- children: [
197- for (var i = 0 ; i < quickItems.length; i++ )
198- SegmentedListTile (
199- isFirst: i == 0 ,
200- tileColor: quickItems[i].isSelected
201- ? context.colors.secondaryContainer
202- : null ,
203- leading: Icon (
204- quickItems[i].icon,
205- fill: 1 ,
206- color: quickItems[i].isSelected
217+ if (quickItems.isNotEmpty) ...[
218+ Padding (
219+ padding: const EdgeInsets .fromLTRB (16 , 8 , 16 , 4 ),
220+ child: Text (
221+ '快速切換' .i18n,
222+ style: context.theme.textTheme.labelMedium? .copyWith (
223+ color: context.colors.onSurfaceVariant,
224+ ),
225+ ),
226+ ),
227+ for (final item in quickItems)
228+ ListTile (
229+ dense: true ,
230+ leading: Icon (
231+ item.icon,
232+ size: 20 ,
233+ color: item.isSelected
234+ ? context.colors.primary
235+ : context.colors.onSurfaceVariant,
236+ ),
237+ title: Text (
238+ item.name,
239+ style: TextStyle (
240+ color: item.isSelected
207241 ? context.colors.primary
208- : context.colors.onSurfaceVariant,
242+ : context.colors.onSurface,
243+ fontWeight: item.isSelected
244+ ? FontWeight .w600
245+ : FontWeight .normal,
209246 ),
210- title: Text (
211- quickItems[i].name,
212- style: TextStyle (
213- color: quickItems[i].isSelected
214- ? context.colors.primary
215- : context.colors.onSurface,
216- fontWeight: quickItems[i].isSelected
217- ? FontWeight .w600
218- : FontWeight .normal,
219- ),
220- ),
221- trailing: quickItems[i].isSelected
222- ? Icon (
223- Symbols .check_rounded,
224- color: context.colors.primary,
225- )
226- : null ,
227- onTap: () => widget.onLocationSelected (quickItems[i].code),
228247 ),
229- SegmentedListTile (
230- isFirst: quickItems.isEmpty,
231- isLast: true ,
232- leading: const Icon (Symbols .add_circle_rounded),
233- title: Text ('新增地點' .i18n),
234- onTap: widget.onAddLocationPressed,
235- ),
236- ],
237- ),
238-
239- SegmentedList .builder (
240- label: Text ('選擇縣市' .i18n),
241- itemCount: cities.length,
242- itemBuilder: (context, index) {
243- final city = cities[index];
244-
245- return SegmentedListTile (
246- isFirst: index == 0 ,
247- isLast: index == cities.length - 1 ,
248- title: Text (city.cityWithLevel),
249- subtitle: currentLocation? .cityWithLevel == city.cityWithLevel
250- ? Text ('目前選擇' .i18n)
248+ trailing: item.isSelected
249+ ? Icon (
250+ Symbols .check_rounded,
251+ size: 20 ,
252+ color: context.colors.primary,
253+ )
251254 : null ,
252- trailing: const Icon (Symbols .chevron_right_rounded),
253- onTap: () => setState (() => _selectedCity = city.cityWithLevel),
254- );
255- },
255+ onTap: () => widget.onLocationSelected (item.code),
256+ ),
257+ const Divider (height: 16 ),
258+ ],
259+ Padding (
260+ padding: const EdgeInsets .fromLTRB (16 , 8 , 16 , 4 ),
261+ child: Text (
262+ '選擇縣市' .i18n,
263+ style: context.theme.textTheme.labelMedium? .copyWith (
264+ color: context.colors.onSurfaceVariant,
265+ ),
266+ ),
256267 ),
268+ for (final city in cities)
269+ ListTile (
270+ dense: true ,
271+ title: Text (city.cityWithLevel),
272+ subtitle: currentLocation? .cityWithLevel == city.cityWithLevel
273+ ? Text (
274+ '目前選擇' .i18n,
275+ style: TextStyle (
276+ color: context.colors.primary,
277+ fontSize: 12 ,
278+ ),
279+ )
280+ : null ,
281+ trailing: const Icon (Symbols .chevron_right_rounded, size: 20 ),
282+ onTap: () => setState (() => _selectedCity = city.cityWithLevel),
283+ ),
257284 ],
258285 );
259286 }
260287
261288 Widget _buildTownList (BuildContext context) {
262289 final towns = _towns;
263290
264- return SegmentedList .builder (
265- label: Text (_selectedCity! ),
291+ return ListView .builder (
292+ shrinkWrap: true ,
293+ padding: const EdgeInsets .only (bottom: 16 ),
266294 itemCount: towns.length,
267295 itemBuilder: (context, index) {
268296 final entry = towns[index];
269297 final code = entry.key;
270298 final town = entry.value;
271299 final isSelected = widget.currentCode == code;
300+ final isSaved = widget.savedCode == code;
301+ final isFavorited = widget.favorited.contains (code);
272302
273- return SegmentedListTile (
274- isFirst: index == 0 ,
275- isLast: index == towns.length - 1 ,
276- tileColor: isSelected ? context.colors.secondaryContainer : null ,
303+ return ListTile (
304+ dense: true ,
305+ leading: isSaved
306+ ? Icon (
307+ Symbols .home_rounded,
308+ size: 20 ,
309+ color: context.colors.onSurfaceVariant,
310+ )
311+ : isFavorited
312+ ? Icon (
313+ Symbols .star_rounded,
314+ size: 20 ,
315+ color: context.colors.onSurfaceVariant,
316+ )
317+ : const SizedBox (width: 20 ),
277318 title: Text (
278319 town.townWithLevel,
279320 style: TextStyle (
@@ -286,6 +327,7 @@ class _LocationMenuSheetState extends State<_LocationMenuSheet> {
286327 trailing: isSelected
287328 ? Icon (
288329 Symbols .check_rounded,
330+ size: 20 ,
289331 color: context.colors.primary,
290332 )
291333 : null ,
0 commit comments