Skip to content

Commit 1489509

Browse files
authored
Persist locations, improve design (#8)
1 parent 1e1cb0f commit 1489509

File tree

8 files changed

+173
-111
lines changed

8 files changed

+173
-111
lines changed

lib/main.dart

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,23 @@ Future<void> main() async {
2121
di.registerSingleton<OpenWeather>(OpenWeather(apiKey: apiKey));
2222
di.registerSingleton<GeoCoder>(GeoCoder());
2323
di.registerSingleton<GeolocatorPlatform>(GeolocatorPlatform.instance);
24+
final locationsService = LocationsService();
25+
await locationsService.init();
2426
di.registerSingleton<LocationsService>(
25-
LocationsService(),
27+
locationsService,
2628
dispose: (s) => s.dispose(),
2729
);
2830
final appModel = AppModel(connectivity: Connectivity());
2931
await appModel.init();
3032
di.registerSingleton(appModel);
31-
final weatherModel = WeatherModel(
32-
locationsService: di<LocationsService>(),
33-
openWeather: di<OpenWeather>(),
34-
geoCoder: di<GeoCoder>(),
35-
geolocatorPlatform: di<GeolocatorPlatform>(),
36-
);
37-
await weatherModel.init();
3833

39-
di.registerSingleton(
40-
weatherModel,
34+
di.registerLazySingleton(
35+
() => WeatherModel(
36+
locationsService: di<LocationsService>(),
37+
openWeather: di<OpenWeather>(),
38+
geoCoder: di<GeoCoder>(),
39+
geolocatorPlatform: di<GeolocatorPlatform>(),
40+
),
4141
dispose: (s) => s.dispose(),
4242
);
4343

lib/src/app/app.dart

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import 'dart:ui';
22

33
import 'package:flutter/material.dart';
4+
import 'package:flutter_weather_bg_null_safety/bg/weather_bg.dart';
45
import 'package:watch_it/watch_it.dart';
56
import 'package:yaru/yaru.dart';
67

8+
import '../../build_context_x.dart';
79
import '../../constants.dart';
810
import '../../weather.dart';
911
import '../weather/view/city_search_field.dart';
1012
import '../weather/weather_model.dart';
13+
import '../weather/weather_utils.dart';
1114
import 'app_model.dart';
1215
import 'offline_page.dart';
1316

@@ -19,9 +22,9 @@ class App extends StatelessWidget {
1922
return MaterialApp(
2023
title: 'Weather',
2124
debugShowCheckedModeBanner: false,
22-
theme: yaruLight,
25+
themeMode: ThemeMode.dark,
2326
darkTheme: yaruDark,
24-
home: const MasterDetailPage(),
27+
home: const AppPage(),
2528
scrollBehavior: const MaterialScrollBehavior().copyWith(
2629
dragDevices: {
2730
PointerDeviceKind.mouse,
@@ -35,30 +38,42 @@ class App extends StatelessWidget {
3538
}
3639
}
3740

38-
class MasterDetailPage extends StatelessWidget with WatchItMixin {
39-
const MasterDetailPage({super.key});
41+
class AppPage extends StatefulWidget with WatchItStatefulWidgetMixin {
42+
const AppPage({super.key});
43+
44+
@override
45+
State<AppPage> createState() => _AppPageState();
46+
}
47+
48+
class _AppPageState extends State<AppPage> {
49+
@override
50+
void initState() {
51+
final lastLocation = di<WeatherModel>().lastLocation;
52+
if (lastLocation != null) {
53+
di<WeatherModel>().loadWeather(cityName: lastLocation);
54+
}
55+
super.initState();
56+
}
4057

4158
@override
4259
Widget build(BuildContext context) {
4360
final isOnline = watchPropertyValue((AppModel m) => m.isOnline);
44-
if (!isOnline) {
45-
return const OfflinePage();
46-
}
61+
4762
final model = di<WeatherModel>();
63+
final mq = context.mq;
64+
final theme = context.theme;
65+
final data = watchPropertyValue((WeatherModel m) => m.data);
4866
final favLocationsLength =
4967
watchPropertyValue((WeatherModel m) => m.favLocations.length);
5068
final favLocations = watchPropertyValue((WeatherModel m) => m.favLocations);
5169
final lastLocation = watchPropertyValue((WeatherModel m) => m.lastLocation);
52-
return YaruMasterDetailPage(
53-
controller: YaruPageController(
54-
length: favLocationsLength == 0 ? 1 : favLocationsLength,
55-
),
56-
layoutDelegate: const YaruMasterFixedPaneDelegate(paneWidth: kPaneWidth),
57-
tileBuilder: (context, index, selected, availableWidth) {
70+
71+
final listView = ListView.builder(
72+
itemCount: favLocationsLength,
73+
itemBuilder: (context, index) {
5874
final location = favLocations.elementAt(index);
5975
return YaruMasterTile(
60-
// TODO: assign pages to location
61-
onTap: () => model.init(cityName: location),
76+
onTap: () => model.loadWeather(cityName: location),
6277
selected: lastLocation == location,
6378
title: Text(
6479
favLocations.elementAt(index),
@@ -70,7 +85,7 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
7085
padding: EdgeInsets.zero,
7186
onPressed: () {
7287
model.removeFavLocation(location).then(
73-
(value) => model.init(
88+
(value) => model.loadWeather(
7489
cityName: favLocations.lastOrNull,
7590
),
7691
);
@@ -83,15 +98,49 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
8398
: null,
8499
);
85100
},
86-
pageBuilder: (context, index) {
87-
return const WeatherPage();
88-
},
89-
appBar: YaruDialogTitleBar(
90-
backgroundColor: YaruMasterDetailTheme.of(context).sideBarColor,
91-
border: BorderSide.none,
92-
style: YaruTitleBarStyle.undecorated,
93-
title: const CitySearchField(),
94-
),
101+
);
102+
103+
return Stack(
104+
children: [
105+
if (data != null)
106+
Opacity(
107+
opacity: 0.6,
108+
child: WeatherBg(
109+
weatherType: getWeatherType(data),
110+
width: mq.size.width,
111+
height: mq.size.height,
112+
),
113+
),
114+
Row(
115+
children: [
116+
Material(
117+
color: theme.colorScheme.surface.withOpacity(0.4),
118+
child: SizedBox(
119+
width: kPaneWidth,
120+
child: Column(
121+
children: [
122+
const YaruDialogTitleBar(
123+
shape: RoundedRectangleBorder(
124+
borderRadius: BorderRadius.only(
125+
topLeft: Radius.circular(kYaruContainerRadius),
126+
),
127+
),
128+
backgroundColor: Colors.transparent,
129+
border: BorderSide.none,
130+
style: YaruTitleBarStyle.undecorated,
131+
title: CitySearchField(),
132+
),
133+
Expanded(child: listView),
134+
],
135+
),
136+
),
137+
),
138+
Expanded(
139+
child: !isOnline ? const OfflinePage() : const WeatherPage(),
140+
),
141+
],
142+
),
143+
],
95144
);
96145
}
97146
}

lib/src/app/offline_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class OfflinePage extends StatelessWidget {
3636
right: 40,
3737
),
3838
child: Text(
39-
"It look's like your computer is not connectedto the internet",
39+
"It look's like your computer is not connected to the internet",
4040
textAlign: TextAlign.center,
4141
style: theme.textTheme.headlineMedium?.copyWith(
4242
color: theme.disabledColor,

lib/src/weather/view/city_search_field.dart

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:watch_it/watch_it.dart';
33
import 'package:yaru/yaru.dart';
44

5+
import '../../../build_context_x.dart';
56
import '../weather_model.dart';
67

78
class CitySearchField extends StatefulWidget {
@@ -31,8 +32,10 @@ class _CitySearchFieldState extends State<CitySearchField> {
3132
@override
3233
Widget build(BuildContext context) {
3334
final model = di<WeatherModel>();
35+
final theme = context.theme;
3436
var textField = TextField(
35-
onSubmitted: (value) => model.init(cityName: _controller.text),
37+
autofocus: true,
38+
onSubmitted: (value) => model.loadWeather(cityName: _controller.text),
3639
controller: _controller,
3740
onTap: () {
3841
_controller.selection = TextSelection(
@@ -45,10 +48,14 @@ class _CitySearchFieldState extends State<CitySearchField> {
4548
.bodyMedium
4649
?.copyWith(fontWeight: FontWeight.w500),
4750
decoration: InputDecoration(
51+
fillColor: theme.colorScheme.onSurface.withOpacity(0.2),
4852
prefixIcon: const Icon(
4953
YaruIcons.search,
5054
size: 15,
5155
),
56+
border: const OutlineInputBorder(borderSide: BorderSide.none),
57+
enabledBorder: const OutlineInputBorder(borderSide: BorderSide.none),
58+
focusedBorder: const OutlineInputBorder(borderSide: BorderSide.none),
5259
prefixIconConstraints:
5360
const BoxConstraints(minWidth: 35, minHeight: 30),
5461
filled: true,
@@ -59,21 +66,21 @@ class _CitySearchFieldState extends State<CitySearchField> {
5966
minWidth: kYaruTitleBarItemHeight,
6067
maxWidth: kYaruTitleBarItemHeight,
6168
),
62-
suffixIcon: ClipRRect(
63-
borderRadius: const BorderRadius.only(
64-
topRight: Radius.circular(kYaruButtonRadius),
65-
bottomRight: Radius.circular(kYaruButtonRadius),
66-
),
67-
child: Material(
68-
color: Colors.transparent,
69-
child: InkWell(
70-
child: const Icon(
71-
YaruIcons.location,
72-
),
73-
onTap: () => model.init(cityName: null),
74-
),
75-
),
76-
),
69+
// suffixIcon: ClipRRect(
70+
// borderRadius: const BorderRadius.only(
71+
// topRight: Radius.circular(kYaruButtonRadius),
72+
// bottomRight: Radius.circular(kYaruButtonRadius),
73+
// ),
74+
// child: Material(
75+
// color: Colors.transparent,
76+
// child: InkWell(
77+
// child: const Icon(
78+
// YaruIcons.location,
79+
// ),
80+
// onTap: () => model.init(cityName: null),
81+
// ),
82+
// ),
83+
// ),
7784
),
7885
);
7986
return textField;

lib/src/weather/view/forecast_tile.dart

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_weather_bg_null_safety/bg/weather_bg.dart';
33
import 'package:flutter_weather_bg_null_safety/flutter_weather_bg.dart';
44
import 'package:open_weather_client/models/weather_data.dart';
5+
import 'package:yaru/constants.dart';
6+
57
import '../../../build_context_x.dart';
6-
import '../weather_utils.dart';
78
import '../../../string_x.dart';
89
import '../weather_data_x.dart';
10+
import '../weather_utils.dart';
911

1012
class ForecastTile extends StatefulWidget {
1113
final WeatherData selectedData;
@@ -30,7 +32,8 @@ class ForecastTile extends StatefulWidget {
3032
this.day,
3133
required this.padding,
3234
this.time,
33-
this.borderRadius = const BorderRadius.all(Radius.circular(10)),
35+
this.borderRadius =
36+
const BorderRadius.all(Radius.circular(kYaruContainerRadius)),
3437
});
3538

3639
@override
@@ -41,7 +44,6 @@ class _ForecastTileState extends State<ForecastTile> {
4144
@override
4245
Widget build(BuildContext context) {
4346
final theme = context.theme;
44-
final light = context.light;
4547
final style = theme.textTheme.headlineSmall?.copyWith(
4648
color: Colors.white,
4749
fontSize: 20,
@@ -115,35 +117,33 @@ class _ForecastTileState extends State<ForecastTile> {
115117
),
116118
];
117119

118-
final banner = Card(
119-
child: Stack(
120-
children: [
121-
Opacity(
122-
opacity: light ? 1 : 0.6,
123-
child: ClipRRect(
124-
borderRadius: widget.borderRadius,
125-
child: WeatherBg(
126-
weatherType: getWeatherType(widget.selectedData),
127-
width: widget.width ?? double.infinity,
128-
height: widget.height ?? double.infinity,
129-
),
120+
final banner = Stack(
121+
children: [
122+
Opacity(
123+
opacity: 0.9,
124+
child: ClipRRect(
125+
borderRadius: widget.borderRadius,
126+
child: WeatherBg(
127+
weatherType: getWeatherType(widget.selectedData),
128+
width: widget.width ?? double.infinity,
129+
height: widget.height ?? double.infinity,
130130
),
131131
),
132-
Center(
133-
child: Padding(
134-
padding: const EdgeInsets.all(20),
135-
child: Wrap(
136-
alignment: WrapAlignment.center,
137-
crossAxisAlignment: WrapCrossAlignment.center,
138-
spacing: 40,
139-
runAlignment: WrapAlignment.center,
140-
runSpacing: 20,
141-
children: children,
142-
),
132+
),
133+
Center(
134+
child: Padding(
135+
padding: const EdgeInsets.all(20),
136+
child: Wrap(
137+
alignment: WrapAlignment.center,
138+
crossAxisAlignment: WrapCrossAlignment.center,
139+
spacing: 40,
140+
runAlignment: WrapAlignment.center,
141+
runSpacing: 20,
142+
children: children,
143143
),
144144
),
145-
],
146-
),
145+
),
146+
],
147147
);
148148

149149
return SizedBox(

lib/src/weather/view/today_tile.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:open_weather_client/models/weather_data.dart';
3+
import 'package:yaru/constants.dart';
34
import '../../../build_context_x.dart';
45
import '../weather_utils.dart';
56
import '../../../string_x.dart';
@@ -39,7 +40,7 @@ class TodayTile extends StatelessWidget {
3940
fontSize: 20,
4041
shadows: [
4142
Shadow(
42-
color: Colors.black.withOpacity(0.9),
43+
color: Colors.black.withOpacity(0.5),
4344
offset: const Offset(0, 1),
4445
blurRadius: 3,
4546
),
@@ -111,7 +112,16 @@ class TodayTile extends StatelessWidget {
111112
),
112113
];
113114

114-
return SizedBox(
115+
return Container(
116+
margin: const EdgeInsets.only(
117+
left: kYaruPagePadding,
118+
right: kYaruPagePadding,
119+
top: kYaruPagePadding,
120+
),
121+
decoration: BoxDecoration(
122+
borderRadius: BorderRadius.circular(kYaruContainerRadius),
123+
color: theme.colorScheme.surface.withOpacity(0.3),
124+
),
115125
width: width,
116126
height: height,
117127
child: Padding(

0 commit comments

Comments
 (0)