Skip to content

Commit 225d0df

Browse files
committed
Add small layout
1 parent 1fe8756 commit 225d0df

File tree

12 files changed

+270
-277
lines changed

12 files changed

+270
-277
lines changed

lib/constants.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const kFavLocationsFileName = 'favlocations.json';
1010
const kLastLocation = 'lastLocation';
1111

1212
const kPaneWidth = 240.0;
13+
const kBreakPoint = 800.0;
1314

1415
const kPagePadding = EdgeInsets.only(
1516
top: kYaruPagePadding,

lib/main.dart

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,29 @@ import 'src/weather/weather_model.dart';
1414

1515
Future<void> main() async {
1616
await YaruWindowTitleBar.ensureInitialized();
17+
1718
final apiKey = await loadApiKey();
18-
if (apiKey != null && apiKey.isNotEmpty) {
19-
di.registerSingleton<OpenWeather>(OpenWeather(apiKey: apiKey));
20-
21-
final locationsService = LocationsService();
22-
await locationsService.init();
23-
di.registerSingleton<LocationsService>(
24-
locationsService,
25-
dispose: (s) => s.dispose(),
26-
);
27-
final appModel = AppModel(connectivity: Connectivity());
28-
await appModel.init();
29-
di.registerSingleton(appModel);
30-
31-
di.registerLazySingleton(
32-
() => WeatherModel(
33-
locationsService: di<LocationsService>(),
34-
openWeather: di<OpenWeather>(),
35-
),
36-
dispose: (s) => s.dispose(),
37-
);
38-
39-
runApp(const App());
40-
} else {
41-
runApp(
42-
MaterialApp(
43-
debugShowCheckedModeBanner: false,
44-
theme: yaruLight,
45-
home: const Scaffold(
46-
appBar: YaruWindowTitleBar(),
47-
body: Center(
48-
child: Text('NO VALID API KEY FOUND'),
49-
),
50-
),
51-
),
52-
);
53-
}
19+
di.registerSingleton<OpenWeather>(OpenWeather(apiKey: apiKey ?? ''));
20+
21+
final locationsService = LocationsService();
22+
await locationsService.init();
23+
di.registerSingleton<LocationsService>(
24+
locationsService,
25+
dispose: (s) => s.dispose(),
26+
);
27+
final appModel = AppModel(connectivity: Connectivity());
28+
await appModel.init();
29+
di.registerSingleton(appModel);
30+
31+
di.registerLazySingleton(
32+
() => WeatherModel(
33+
locationsService: di<LocationsService>(),
34+
openWeather: di<OpenWeather>(),
35+
),
36+
dispose: (s) => s.dispose(),
37+
);
38+
39+
runApp(const App());
5440
}
5541

5642
Future<String?> loadApiKey() async {

lib/src/app/app.dart

Lines changed: 25 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,20 @@ import 'package:flutter_weather_bg_null_safety/bg/weather_bg.dart';
55
import 'package:watch_it/watch_it.dart';
66
import 'package:yaru/yaru.dart';
77

8-
import '../build_context_x.dart';
98
import '../../constants.dart';
109
import '../../weather.dart';
11-
import '../weather/view/city_search_field.dart';
12-
import '../weather/weather_data_x.dart';
1310
import '../weather/weather_model.dart';
1411
import 'app_model.dart';
1512
import 'offline_page.dart';
13+
import 'side_bar.dart';
1614

1715
class App extends StatelessWidget {
1816
const App({super.key});
1917

2018
@override
2119
Widget build(BuildContext context) {
2220
return MaterialApp(
23-
title: 'Weather',
21+
title: kAppTitle,
2422
debugShowCheckedModeBanner: false,
2523
theme: yaruLight,
2624
darkTheme: yaruDark.copyWith(
@@ -63,88 +61,35 @@ class _AppPageState extends State<AppPage> {
6361
Widget build(BuildContext context) {
6462
final isOnline = watchPropertyValue((AppModel m) => m.isOnline);
6563

66-
final model = di<WeatherModel>();
67-
final mq = context.mq;
68-
final theme = context.theme;
69-
final data = watchPropertyValue((WeatherModel m) => m.data);
70-
final favLocationsLength =
71-
watchPropertyValue((WeatherModel m) => m.favLocations.length);
72-
final favLocations = watchPropertyValue((WeatherModel m) => m.favLocations);
73-
final lastLocation = watchPropertyValue((WeatherModel m) => m.lastLocation);
64+
final weatherType = watchPropertyValue((WeatherModel m) => m.weatherType);
7465

75-
final listView = ListView.builder(
76-
itemCount: favLocationsLength,
77-
itemBuilder: (context, index) {
78-
final location = favLocations.elementAt(index);
79-
return YaruMasterTile(
80-
onTap: () => model.loadWeather(cityName: location),
81-
selected: lastLocation == location,
82-
title: Text(
83-
favLocations.elementAt(index),
84-
),
85-
trailing: favLocationsLength > 1
86-
? Center(
87-
widthFactor: 0.1,
88-
child: IconButton(
89-
padding: EdgeInsets.zero,
90-
onPressed: () {
91-
model.removeFavLocation(location).then(
92-
(value) => model.loadWeather(
93-
cityName: favLocations.lastOrNull,
94-
),
95-
);
96-
},
97-
icon: const Icon(
98-
YaruIcons.window_close,
99-
),
100-
),
101-
)
102-
: null,
103-
);
104-
},
105-
);
106-
107-
return Stack(
108-
children: [
109-
if (data != null)
110-
Opacity(
111-
opacity: 0.6,
112-
child: WeatherBg(
113-
weatherType: data.weatherType,
114-
width: mq.size.width,
115-
height: mq.size.height,
116-
),
117-
),
118-
Row(
66+
return LayoutBuilder(
67+
builder: (context, constraints) {
68+
return Stack(
11969
children: [
120-
Material(
121-
color: theme.colorScheme.surface.withOpacity(0.4),
122-
child: SizedBox(
123-
width: kPaneWidth,
124-
child: Column(
125-
children: [
126-
const YaruDialogTitleBar(
127-
shape: RoundedRectangleBorder(
128-
borderRadius: BorderRadius.only(
129-
topLeft: Radius.circular(kYaruContainerRadius),
130-
),
131-
),
132-
backgroundColor: Colors.transparent,
133-
border: BorderSide.none,
134-
style: YaruTitleBarStyle.undecorated,
135-
title: CitySearchField(),
136-
),
137-
Expanded(child: listView),
138-
],
139-
),
70+
Opacity(
71+
opacity: 0.7,
72+
child: WeatherBg(
73+
weatherType: weatherType,
74+
width: constraints.maxWidth,
75+
height: constraints.maxHeight,
14076
),
14177
),
142-
Expanded(
143-
child: !isOnline ? const OfflinePage() : const WeatherPage(),
78+
Row(
79+
children: [
80+
if (constraints.maxWidth > kBreakPoint) const SideBar(),
81+
Expanded(
82+
child: !isOnline
83+
? const OfflinePage()
84+
: WeatherPage(
85+
showDrawer: constraints.maxWidth < kBreakPoint,
86+
),
87+
),
88+
],
14489
),
14590
],
146-
),
147-
],
91+
);
92+
},
14893
);
14994
}
15095
}

lib/src/app/offline_page.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class OfflinePage extends StatelessWidget {
1010
Widget build(BuildContext context) {
1111
final theme = context.theme;
1212
return YaruDetailPage(
13+
backgroundColor: Colors.transparent,
1314
appBar: YaruWindowTitleBar(
1415
border: BorderSide.none,
1516
title: const Text('Offline'),

lib/src/app/side_bar.dart

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:watch_it/watch_it.dart';
3+
import 'package:yaru/yaru.dart';
4+
5+
import '../../constants.dart';
6+
import '../build_context_x.dart';
7+
import '../weather/view/city_search_field.dart';
8+
import '../weather/weather_model.dart';
9+
10+
class SideBar extends StatelessWidget with WatchItMixin {
11+
const SideBar({super.key, this.onSelected});
12+
13+
final VoidCallback? onSelected;
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
final theme = context.theme;
18+
19+
final model = di<WeatherModel>();
20+
21+
final favLocationsLength =
22+
watchPropertyValue((WeatherModel m) => m.favLocations.length);
23+
final favLocations = watchPropertyValue((WeatherModel m) => m.favLocations);
24+
final lastLocation = watchPropertyValue((WeatherModel m) => m.lastLocation);
25+
26+
final listView = ListView.builder(
27+
itemCount: favLocationsLength,
28+
itemBuilder: (context, index) {
29+
final location = favLocations.elementAt(index);
30+
return YaruMasterTile(
31+
onTap: () {
32+
model.loadWeather(cityName: location);
33+
onSelected?.call();
34+
},
35+
selected: lastLocation == location,
36+
title: Text(
37+
favLocations.elementAt(index),
38+
),
39+
trailing: favLocationsLength > 1
40+
? Center(
41+
widthFactor: 0.1,
42+
child: IconButton(
43+
padding: EdgeInsets.zero,
44+
onPressed: () {
45+
model.removeFavLocation(location).then(
46+
(value) => model.loadWeather(
47+
cityName: favLocations.lastOrNull,
48+
),
49+
);
50+
},
51+
icon: const Icon(
52+
YaruIcons.window_close,
53+
),
54+
),
55+
)
56+
: null,
57+
);
58+
},
59+
);
60+
61+
return Material(
62+
color: theme.colorScheme.surface.withOpacity(0.4),
63+
child: SizedBox(
64+
width: kPaneWidth,
65+
child: Column(
66+
children: [
67+
const YaruDialogTitleBar(
68+
shape: RoundedRectangleBorder(
69+
borderRadius: BorderRadius.only(
70+
topLeft: Radius.circular(kYaruContainerRadius),
71+
),
72+
),
73+
backgroundColor: Colors.transparent,
74+
border: BorderSide.none,
75+
style: YaruTitleBarStyle.undecorated,
76+
title: CitySearchField(),
77+
),
78+
Expanded(child: listView),
79+
],
80+
),
81+
),
82+
);
83+
}
84+
}

lib/src/weather/view/forecast_chart.dart

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,50 +17,59 @@ class ForeCastChart extends StatelessWidget with WatchItMixin {
1717

1818
@override
1919
Widget build(BuildContext context) {
20-
final data =
20+
final notTodayForecastDaily =
2121
watchPropertyValue((WeatherModel m) => m.notTodayForecastDaily);
22+
2223
final error = watchPropertyValue((WeatherModel m) => m.error);
23-
return error != null
24-
? Center(
25-
child: Text(error),
26-
)
27-
: data == null
24+
25+
return Center(
26+
child: Container(
27+
margin: kPagePadding,
28+
decoration: BoxDecoration(
29+
borderRadius: BorderRadius.circular(kYaruContainerRadius),
30+
color: context.theme.colorScheme.surface.withOpacity(0.3),
31+
),
32+
width: context.mq.size.width,
33+
child: (error != null)
2834
? Center(
29-
child: YaruCircularProgressIndicator(
30-
color: context.theme.colorScheme.onSurface,
31-
strokeWidth: 3,
35+
child: Padding(
36+
padding: const EdgeInsets.all(kYaruPagePadding),
37+
child: Text(
38+
error,
39+
textAlign: TextAlign.center,
40+
),
3241
),
3342
)
34-
: Center(
35-
child: Container(
36-
margin: kPagePadding,
37-
decoration: BoxDecoration(
38-
borderRadius: BorderRadius.circular(kYaruContainerRadius),
39-
color: context.theme.colorScheme.surface.withOpacity(0.3),
40-
),
41-
height: context.mq.size.height -
42-
kYaruTitleBarHeight -
43-
3 * kYaruPagePadding,
44-
width:
45-
context.mq.size.width - kPaneWidth - 2 * kYaruPagePadding,
46-
child: Padding(
43+
: (notTodayForecastDaily == null)
44+
? Center(
45+
child: YaruCircularProgressIndicator(
46+
color: context.theme.colorScheme.onSurface,
47+
strokeWidth: 3,
48+
),
49+
)
50+
: Padding(
4751
padding:
4852
const EdgeInsets.symmetric(vertical: kYaruPagePadding),
4953
child: BarChart(
5054
BarChartData(
5155
baselineY: 0,
52-
titlesData: getTitlesData(context, data),
56+
titlesData:
57+
getTitlesData(context, notTodayForecastDaily + []),
5358
borderData: borderData,
54-
barGroups: getBarGroups(data),
59+
barGroups: getBarGroups(notTodayForecastDaily),
5560
gridData: const FlGridData(show: false),
5661
alignment: BarChartAlignment.spaceAround,
57-
maxY: data.map((e) => e.temperature.tempMax).max,
58-
minY: data.map((e) => e.temperature.tempMin).min,
62+
maxY: notTodayForecastDaily
63+
.map((e) => e.temperature.tempMax)
64+
.max,
65+
minY: notTodayForecastDaily
66+
.map((e) => e.temperature.tempMin)
67+
.min,
5968
),
6069
),
6170
),
62-
),
63-
);
71+
),
72+
);
6473
}
6574

6675
FlTitlesData getTitlesData(BuildContext context, List<WeatherData> data) =>

0 commit comments

Comments
 (0)