Skip to content

Commit

Permalink
Persist locations, improve design (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feichtmeier authored Apr 25, 2024
1 parent 1e1cb0f commit 1489509
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 111 deletions.
20 changes: 10 additions & 10 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ Future<void> main() async {
di.registerSingleton<OpenWeather>(OpenWeather(apiKey: apiKey));
di.registerSingleton<GeoCoder>(GeoCoder());
di.registerSingleton<GeolocatorPlatform>(GeolocatorPlatform.instance);
final locationsService = LocationsService();
await locationsService.init();
di.registerSingleton<LocationsService>(
LocationsService(),
locationsService,
dispose: (s) => s.dispose(),
);
final appModel = AppModel(connectivity: Connectivity());
await appModel.init();
di.registerSingleton(appModel);
final weatherModel = WeatherModel(
locationsService: di<LocationsService>(),
openWeather: di<OpenWeather>(),
geoCoder: di<GeoCoder>(),
geolocatorPlatform: di<GeolocatorPlatform>(),
);
await weatherModel.init();

di.registerSingleton(
weatherModel,
di.registerLazySingleton(
() => WeatherModel(
locationsService: di<LocationsService>(),
openWeather: di<OpenWeather>(),
geoCoder: di<GeoCoder>(),
geolocatorPlatform: di<GeolocatorPlatform>(),
),
dispose: (s) => s.dispose(),
);

Expand Down
99 changes: 74 additions & 25 deletions lib/src/app/app.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_weather_bg_null_safety/bg/weather_bg.dart';
import 'package:watch_it/watch_it.dart';
import 'package:yaru/yaru.dart';

import '../../build_context_x.dart';
import '../../constants.dart';
import '../../weather.dart';
import '../weather/view/city_search_field.dart';
import '../weather/weather_model.dart';
import '../weather/weather_utils.dart';
import 'app_model.dart';
import 'offline_page.dart';

Expand All @@ -19,9 +22,9 @@ class App extends StatelessWidget {
return MaterialApp(
title: 'Weather',
debugShowCheckedModeBanner: false,
theme: yaruLight,
themeMode: ThemeMode.dark,
darkTheme: yaruDark,
home: const MasterDetailPage(),
home: const AppPage(),
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
Expand All @@ -35,30 +38,42 @@ class App extends StatelessWidget {
}
}

class MasterDetailPage extends StatelessWidget with WatchItMixin {
const MasterDetailPage({super.key});
class AppPage extends StatefulWidget with WatchItStatefulWidgetMixin {
const AppPage({super.key});

@override
State<AppPage> createState() => _AppPageState();
}

class _AppPageState extends State<AppPage> {
@override
void initState() {
final lastLocation = di<WeatherModel>().lastLocation;
if (lastLocation != null) {
di<WeatherModel>().loadWeather(cityName: lastLocation);
}
super.initState();
}

@override
Widget build(BuildContext context) {
final isOnline = watchPropertyValue((AppModel m) => m.isOnline);
if (!isOnline) {
return const OfflinePage();
}

final model = di<WeatherModel>();
final mq = context.mq;
final theme = context.theme;
final data = watchPropertyValue((WeatherModel m) => m.data);
final favLocationsLength =
watchPropertyValue((WeatherModel m) => m.favLocations.length);
final favLocations = watchPropertyValue((WeatherModel m) => m.favLocations);
final lastLocation = watchPropertyValue((WeatherModel m) => m.lastLocation);
return YaruMasterDetailPage(
controller: YaruPageController(
length: favLocationsLength == 0 ? 1 : favLocationsLength,
),
layoutDelegate: const YaruMasterFixedPaneDelegate(paneWidth: kPaneWidth),
tileBuilder: (context, index, selected, availableWidth) {

final listView = ListView.builder(
itemCount: favLocationsLength,
itemBuilder: (context, index) {
final location = favLocations.elementAt(index);
return YaruMasterTile(
// TODO: assign pages to location
onTap: () => model.init(cityName: location),
onTap: () => model.loadWeather(cityName: location),
selected: lastLocation == location,
title: Text(
favLocations.elementAt(index),
Expand All @@ -70,7 +85,7 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
padding: EdgeInsets.zero,
onPressed: () {
model.removeFavLocation(location).then(
(value) => model.init(
(value) => model.loadWeather(
cityName: favLocations.lastOrNull,
),
);
Expand All @@ -83,15 +98,49 @@ class MasterDetailPage extends StatelessWidget with WatchItMixin {
: null,
);
},
pageBuilder: (context, index) {
return const WeatherPage();
},
appBar: YaruDialogTitleBar(
backgroundColor: YaruMasterDetailTheme.of(context).sideBarColor,
border: BorderSide.none,
style: YaruTitleBarStyle.undecorated,
title: const CitySearchField(),
),
);

return Stack(
children: [
if (data != null)
Opacity(
opacity: 0.6,
child: WeatherBg(
weatherType: getWeatherType(data),
width: mq.size.width,
height: mq.size.height,
),
),
Row(
children: [
Material(
color: theme.colorScheme.surface.withOpacity(0.4),
child: SizedBox(
width: kPaneWidth,
child: Column(
children: [
const YaruDialogTitleBar(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kYaruContainerRadius),
),
),
backgroundColor: Colors.transparent,
border: BorderSide.none,
style: YaruTitleBarStyle.undecorated,
title: CitySearchField(),
),
Expanded(child: listView),
],
),
),
),
Expanded(
child: !isOnline ? const OfflinePage() : const WeatherPage(),
),
],
),
],
);
}
}
2 changes: 1 addition & 1 deletion lib/src/app/offline_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class OfflinePage extends StatelessWidget {
right: 40,
),
child: Text(
"It look's like your computer is not connectedto the internet",
"It look's like your computer is not connected to the internet",
textAlign: TextAlign.center,
style: theme.textTheme.headlineMedium?.copyWith(
color: theme.disabledColor,
Expand Down
39 changes: 23 additions & 16 deletions lib/src/weather/view/city_search_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';
import 'package:yaru/yaru.dart';

import '../../../build_context_x.dart';
import '../weather_model.dart';

class CitySearchField extends StatefulWidget {
Expand Down Expand Up @@ -31,8 +32,10 @@ class _CitySearchFieldState extends State<CitySearchField> {
@override
Widget build(BuildContext context) {
final model = di<WeatherModel>();
final theme = context.theme;
var textField = TextField(
onSubmitted: (value) => model.init(cityName: _controller.text),
autofocus: true,
onSubmitted: (value) => model.loadWeather(cityName: _controller.text),
controller: _controller,
onTap: () {
_controller.selection = TextSelection(
Expand All @@ -45,10 +48,14 @@ class _CitySearchFieldState extends State<CitySearchField> {
.bodyMedium
?.copyWith(fontWeight: FontWeight.w500),
decoration: InputDecoration(
fillColor: theme.colorScheme.onSurface.withOpacity(0.2),
prefixIcon: const Icon(
YaruIcons.search,
size: 15,
),
border: const OutlineInputBorder(borderSide: BorderSide.none),
enabledBorder: const OutlineInputBorder(borderSide: BorderSide.none),
focusedBorder: const OutlineInputBorder(borderSide: BorderSide.none),
prefixIconConstraints:
const BoxConstraints(minWidth: 35, minHeight: 30),
filled: true,
Expand All @@ -59,21 +66,21 @@ class _CitySearchFieldState extends State<CitySearchField> {
minWidth: kYaruTitleBarItemHeight,
maxWidth: kYaruTitleBarItemHeight,
),
suffixIcon: ClipRRect(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(kYaruButtonRadius),
bottomRight: Radius.circular(kYaruButtonRadius),
),
child: Material(
color: Colors.transparent,
child: InkWell(
child: const Icon(
YaruIcons.location,
),
onTap: () => model.init(cityName: null),
),
),
),
// suffixIcon: ClipRRect(
// borderRadius: const BorderRadius.only(
// topRight: Radius.circular(kYaruButtonRadius),
// bottomRight: Radius.circular(kYaruButtonRadius),
// ),
// child: Material(
// color: Colors.transparent,
// child: InkWell(
// child: const Icon(
// YaruIcons.location,
// ),
// onTap: () => model.init(cityName: null),
// ),
// ),
// ),
),
);
return textField;
Expand Down
56 changes: 28 additions & 28 deletions lib/src/weather/view/forecast_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_weather_bg_null_safety/bg/weather_bg.dart';
import 'package:flutter_weather_bg_null_safety/flutter_weather_bg.dart';
import 'package:open_weather_client/models/weather_data.dart';
import 'package:yaru/constants.dart';

import '../../../build_context_x.dart';
import '../weather_utils.dart';
import '../../../string_x.dart';
import '../weather_data_x.dart';
import '../weather_utils.dart';

class ForecastTile extends StatefulWidget {
final WeatherData selectedData;
Expand All @@ -30,7 +32,8 @@ class ForecastTile extends StatefulWidget {
this.day,
required this.padding,
this.time,
this.borderRadius = const BorderRadius.all(Radius.circular(10)),
this.borderRadius =
const BorderRadius.all(Radius.circular(kYaruContainerRadius)),
});

@override
Expand All @@ -41,7 +44,6 @@ class _ForecastTileState extends State<ForecastTile> {
@override
Widget build(BuildContext context) {
final theme = context.theme;
final light = context.light;
final style = theme.textTheme.headlineSmall?.copyWith(
color: Colors.white,
fontSize: 20,
Expand Down Expand Up @@ -115,35 +117,33 @@ class _ForecastTileState extends State<ForecastTile> {
),
];

final banner = Card(
child: Stack(
children: [
Opacity(
opacity: light ? 1 : 0.6,
child: ClipRRect(
borderRadius: widget.borderRadius,
child: WeatherBg(
weatherType: getWeatherType(widget.selectedData),
width: widget.width ?? double.infinity,
height: widget.height ?? double.infinity,
),
final banner = Stack(
children: [
Opacity(
opacity: 0.9,
child: ClipRRect(
borderRadius: widget.borderRadius,
child: WeatherBg(
weatherType: getWeatherType(widget.selectedData),
width: widget.width ?? double.infinity,
height: widget.height ?? double.infinity,
),
),
Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 40,
runAlignment: WrapAlignment.center,
runSpacing: 20,
children: children,
),
),
Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 40,
runAlignment: WrapAlignment.center,
runSpacing: 20,
children: children,
),
),
],
),
),
],
);

return SizedBox(
Expand Down
14 changes: 12 additions & 2 deletions lib/src/weather/view/today_tile.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:open_weather_client/models/weather_data.dart';
import 'package:yaru/constants.dart';
import '../../../build_context_x.dart';
import '../weather_utils.dart';
import '../../../string_x.dart';
Expand Down Expand Up @@ -39,7 +40,7 @@ class TodayTile extends StatelessWidget {
fontSize: 20,
shadows: [
Shadow(
color: Colors.black.withOpacity(0.9),
color: Colors.black.withOpacity(0.5),
offset: const Offset(0, 1),
blurRadius: 3,
),
Expand Down Expand Up @@ -111,7 +112,16 @@ class TodayTile extends StatelessWidget {
),
];

return SizedBox(
return Container(
margin: const EdgeInsets.only(
left: kYaruPagePadding,
right: kYaruPagePadding,
top: kYaruPagePadding,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(kYaruContainerRadius),
color: theme.colorScheme.surface.withOpacity(0.3),
),
width: width,
height: height,
child: Padding(
Expand Down
Loading

0 comments on commit 1489509

Please sign in to comment.