From 848b76f85e62cbd2d01d042832adfa0f5d440934 Mon Sep 17 00:00:00 2001 From: Frederik Feichtmeier Date: Tue, 18 Feb 2025 20:15:19 +0100 Subject: [PATCH] feat: flutter upgrade and add location autocomplete (#15) --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yaml | 18 ++ .github/workflows/snap.yaml | 2 +- .gitignore | 2 + devtools_options.yaml | 3 + lib/app/side_bar.dart | 125 +++++++---- lib/weather/view/city_search_field.dart | 209 +++++++++++------- lib/weather/weather_data_x.dart | 1 + lib/weather/weather_model.dart | 44 ++++ macos/Podfile.lock | 29 ++- macos/Runner.xcodeproj/project.pbxproj | 13 +- .../xcshareddata/xcschemes/Runner.xcscheme | 1 + macos/Runner/AppDelegate.swift | 6 +- macos/Runner/Configs/AppInfo.xcconfig | 2 +- macos/Runner/DebugProfile.entitlements | 2 + macos/Runner/Release.entitlements | 2 + pubspec.lock | 98 ++++---- pubspec.yaml | 3 +- snap/snapcraft.yaml | 8 +- 19 files changed, 388 insertions(+), 182 deletions(-) create mode 100644 .github/workflows/release.yaml create mode 100644 devtools_options.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34c70d5..195d26b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: branches: [master] env: - FLUTTER_VERSION: '3.27.4' + FLUTTER_VERSION: '3.29.0' jobs: analyze: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..e431cc8 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,18 @@ +name: Release + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + release: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + release-type: dart \ No newline at end of file diff --git a/.github/workflows/snap.yaml b/.github/workflows/snap.yaml index 4b2dbf7..714df2d 100644 --- a/.github/workflows/snap.yaml +++ b/.github/workflows/snap.yaml @@ -7,7 +7,7 @@ on: workflow_dispatch: env: - FLUTTER_VERSION: "3.27.4" + FLUTTER_VERSION: "3.29.0" jobs: build_and_release_linux_snap_edge_amd64: diff --git a/.gitignore b/.gitignore index 2f36ff0..28a8f64 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/app/side_bar.dart b/lib/app/side_bar.dart index 497ce27..118360f 100644 --- a/lib/app/side_bar.dart +++ b/lib/app/side_bar.dart @@ -7,60 +7,41 @@ import '../extensions/build_context_x.dart'; import '../weather/view/city_search_field.dart'; import '../weather/weather_model.dart'; -class SideBar extends StatelessWidget with WatchItMixin { +class SideBar extends StatefulWidget with WatchItStatefulWidgetMixin { const SideBar({super.key, this.onSelected}); final VoidCallback? onSelected; + @override + State createState() => _SideBarState(); +} + +class _SideBarState extends State { @override Widget build(BuildContext context) { final theme = context.theme; - final model = di(); - final favLocationsLength = watchPropertyValue((WeatherModel m) => m.favLocations.length); final favLocations = watchPropertyValue((WeatherModel m) => m.favLocations); - final lastLocation = watchPropertyValue((WeatherModel m) => m.lastLocation); + final currentLocation = + watchPropertyValue((WeatherModel m) => m.lastLocation); final listView = ListView.builder( itemCount: favLocationsLength, itemBuilder: (context, index) { - final location = favLocations.elementAt(index); - return Stack( - alignment: Alignment.centerRight, - children: [ - YaruMasterTile( - onTap: () { - model.loadWeather(cityName: location); - onSelected?.call(); - }, - selected: lastLocation == location, - title: Text( - favLocations.elementAt(index), - ), - ), - if (favLocationsLength > 1 && lastLocation == location) - Positioned( - right: 20, - child: SizedBox.square( - dimension: 30, - child: IconButton( - padding: EdgeInsets.zero, - onPressed: () { - model.removeFavLocation(location).then( - (value) => model.loadWeather( - cityName: favLocations.lastOrNull, - ), - ); - }, - icon: const Icon( - YaruIcons.window_close, - ), - ), - ), - ), - ], + final selected = currentLocation == favLocations.elementAt(index); + return Tile( + selected: selected, + location: favLocations.elementAt(index), + onClear: favLocationsLength > 1 + ? () => di().loadWeather( + cityName: selected + ? favLocations.elementAtOrNull(index - 1) + : currentLocation, + ) + : null, + onSelected: widget.onSelected, ); }, ); @@ -89,3 +70,69 @@ class SideBar extends StatelessWidget with WatchItMixin { ); } } + +class Tile extends StatefulWidget { + const Tile({ + super.key, + required this.location, + required this.onSelected, + required this.onClear, + required this.selected, + }); + + final String location; + final VoidCallback? onSelected; + final bool selected; + final Function()? onClear; + + @override + State createState() => _TileState(); +} + +class _TileState extends State { + bool _hovered = false; + + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (_) => setState(() => _hovered = true), + onExit: (_) => setState(() => _hovered = false), + child: Stack( + alignment: Alignment.centerRight, + children: [ + Tooltip( + message: widget.location, + child: YaruMasterTile( + onTap: () { + di().loadWeather(cityName: widget.location); + widget.onSelected?.call(); + }, + selected: widget.selected, + title: Text( + widget.location, + ), + ), + ), + if (widget.onClear != null && (_hovered || widget.selected)) + Positioned( + right: 20, + child: SizedBox.square( + dimension: 30, + child: IconButton( + padding: EdgeInsets.zero, + onPressed: () { + di().removeFavLocation(widget.location).then( + (_) => widget.onClear?.call(), + ); + }, + icon: const Icon( + YaruIcons.window_close, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/weather/view/city_search_field.dart b/lib/weather/view/city_search_field.dart index 2e78f7a..1c7109a 100644 --- a/lib/weather/view/city_search_field.dart +++ b/lib/weather/view/city_search_field.dart @@ -1,13 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:watch_it/watch_it.dart'; import 'package:yaru/yaru.dart'; -import '../../extensions/string_x.dart'; +import '../../constants.dart'; import '../../extensions/build_context_x.dart'; +import '../../extensions/string_x.dart'; import '../../l10n/l10n.dart'; import '../weather_model.dart'; -class CitySearchField extends StatefulWidget with WatchItStatefulWidgetMixin { +class CitySearchField extends StatelessWidget with WatchItMixin { const CitySearchField({ super.key, this.watchError = false, @@ -15,88 +17,147 @@ class CitySearchField extends StatefulWidget with WatchItStatefulWidgetMixin { final bool watchError; - @override - State createState() => _CitySearchFieldState(); -} - -class _CitySearchFieldState extends State { - late TextEditingController _controller; - - @override - void initState() { - super.initState(); - _controller = TextEditingController(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { - final model = di(); final error = watchPropertyValue((WeatherModel m) => m.error); final loading = watchPropertyValue((WeatherModel m) => m.loading); final theme = context.theme; - var textField = TextField( - autofocus: true, - onSubmitted: (value) => model.loadWeather(cityName: _controller.text), - controller: _controller, - onTap: () { - _controller.selection = TextSelection( - baseOffset: 0, - extentOffset: _controller.value.text.length, + + return Autocomplete( + fieldViewBuilder: (context, controller, focusNode, onFieldSubmitted) => + TextField( + maxLines: 1, + autofocus: true, + focusNode: focusNode, + controller: controller, + onSubmitted: (v) => di().loadWeather(cityName: v), + onTap: () { + controller.selection = TextSelection( + baseOffset: 0, + extentOffset: controller.value.text.length, + ); + }, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w500), + decoration: InputDecoration( + fillColor: theme.colorScheme.onSurface.withValues(alpha: 0.2), + prefixIcon: Icon( + YaruIcons.search, + size: 15, + color: theme.colorScheme.onSurface, + ), + 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, + hintText: context.l10n.cityName, + errorText: watchError + ? (error?.cityNotFound == true + ? context.l10n.cityNotFound + : error?.emptyCity == true + ? context.l10n.enterACityName + : error) + : null, + errorMaxLines: 10, + suffixIconConstraints: const BoxConstraints( + maxHeight: 20, + minHeight: 20, + minWidth: 20, + maxWidth: 20, + ), + suffixIcon: loading + ? Padding( + padding: const EdgeInsets.only(right: 8), + child: SizedBox.square( + dimension: 20, + child: YaruCircularProgressIndicator( + color: context.theme.colorScheme.onSurface, + strokeWidth: 1, + ), + ), + ) + : null, + ), + ), + onSelected: (o) => di().loadWeather(cityName: o), + optionsBuilder: (v) async { + if (v.text.isEmpty) { + return []; + } + + return di().findCityNames( + v.text, + onError: (error) => ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(error))), ); }, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(fontWeight: FontWeight.w500), - decoration: InputDecoration( - fillColor: theme.colorScheme.onSurface.withValues(alpha: 0.2), - prefixIcon: Icon( - YaruIcons.search, - size: 15, - color: theme.colorScheme.onSurface, - ), - 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, - hintText: context.l10n.cityName, - errorText: widget.watchError - ? (error?.cityNotFound == true - ? context.l10n.cityNotFound - : error?.emptyCity == true - ? context.l10n.enterACityName - : error) - : null, - errorMaxLines: 10, - suffixIconConstraints: const BoxConstraints( - maxHeight: 20, - minHeight: 20, - minWidth: 20, - maxWidth: 20, - ), - suffixIcon: loading - ? Padding( - padding: const EdgeInsets.only(right: 8), - child: SizedBox.square( - dimension: 20, - child: YaruCircularProgressIndicator( - color: context.theme.colorScheme.onSurface, - strokeWidth: 1, + optionsViewBuilder: (context, onSelected, options) { + return Align( + alignment: Alignment.topLeft, + child: SizedBox( + width: kPaneWidth - 30, + height: (options.length * 60) > 400 ? 400 : options.length * 60, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Material( + color: theme.popupMenuTheme.color, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + side: BorderSide( + color: theme.dividerColor, + width: 1, ), ), - ) - : null, - ), + elevation: 1, + child: ListView.builder( + itemCount: options.length, + itemBuilder: (context, index) { + return Builder( + builder: (BuildContext context) { + final bool highlight = AutocompleteHighlightedOption.of( + context, + ) == + index; + if (highlight) { + SchedulerBinding.instance + .addPostFrameCallback((Duration timeStamp) { + Scrollable.ensureVisible( + context, + alignment: 0.5, + ); + }); + } + final e = options.elementAt(index).split(','); + + return ListTile( + title: Text( + (e.firstOrNull ?? '').trim(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + ((e.elementAtOrNull(1) ?? '') + + (e.elementAtOrNull(2) ?? '')) + .trim(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + onTap: () => onSelected(options.elementAt(index)), + ); + }, + ); + }, + ), + ), + ), + ), + ); + }, ); - return textField; } } diff --git a/lib/weather/weather_data_x.dart b/lib/weather/weather_data_x.dart index 0317380..aabdf08 100644 --- a/lib/weather/weather_data_x.dart +++ b/lib/weather/weather_data_x.dart @@ -31,6 +31,7 @@ extension WeatherDataX on WeatherData { 'Clear' => night ? WeatherType.sunnyNight : WeatherType.sunny, 'Sunny' => night ? WeatherType.sunnyNight : WeatherType.sunny, 'Wind' => night ? WeatherType.dusty : WeatherType.dusty, + 'Mist' => WeatherType.foggy, _ => WeatherType.thunder } }; diff --git a/lib/weather/weather_model.dart b/lib/weather/weather_model.dart index b826684..a4941a9 100644 --- a/lib/weather/weather_model.dart +++ b/lib/weather/weather_model.dart @@ -1,8 +1,10 @@ // ignore_for_file: unused_element import 'dart:async'; +import 'dart:convert'; import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_weather_bg_null_safety/utils/weather_type.dart'; import 'package:open_weather_client/models/temperature.dart'; import 'package:open_weather_client/open_weather.dart'; @@ -12,6 +14,7 @@ import 'package:watch_it/watch_it.dart'; import '../locations/locations_service.dart'; import 'weather_data_x.dart'; import 'weekday.dart'; +import 'package:http/http.dart' as http; class WeatherModel extends SafeChangeNotifier { WeatherModel({ @@ -102,6 +105,47 @@ class WeatherModel extends SafeChangeNotifier { } } + Future> findCityNames( + String query, { + required void Function(String error) onError, + }) async { + final String apiUrl = + 'http://api.openweathermap.org/geo/1.0/direct?q=$query&limit=5&appid=${_openWeather.apiKey}&lang=${WidgetsBinding.instance.platformDispatcher.locale.countryCode?.toLowerCase()}'; + + try { + final response = await http.get(Uri.parse(apiUrl)); + + if (response.statusCode == 200) { + final List jsonData = jsonDecode(response.body); + List cityNames = []; + + for (var item in jsonData) { + if (item is Map && item.containsKey('name')) { + String? cityName = item['name']; + String? country = item['country']; + String? state = item['state']; + + String formattedName = cityName ?? ''; + if (state != null) { + formattedName += ', $state'; + } + if (country != null) { + formattedName += ', $country'; + } + cityNames.add(formattedName); + } + } + return cityNames; + } else { + onError('Error: ${response.statusCode}, ${response.body}'); + return []; + } + } catch (e) { + onError('Error: $e'); + return []; + } + } + Future?>? loadForeCastByCityName({ required String cityName, }) async { diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 154e304..f27e9db 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,35 +1,48 @@ PODS: + - flutter_secure_storage_macos (6.1.3): + - FlutterMacOS - FlutterMacOS (1.0.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - screen_retriever (0.0.1): + - screen_retriever_macos (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter - FlutterMacOS - window_manager (0.2.0): - FlutterMacOS DEPENDENCIES: + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) + - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) EXTERNAL SOURCES: + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: :path: Flutter/ephemeral path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin - screen_retriever: - :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin window_manager: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: + flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index d70f3e5..23b0eb2 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -204,7 +204,6 @@ E5B14AA1F26E0A12B4EAC1F8 /* Pods-RunnerTests.release.xcconfig */, 32904D7BF5C88F026B012E4B /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -479,7 +478,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.pulse.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = org.feichtmeier.pulse.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pulse.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pulse"; @@ -494,7 +493,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.pulse.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = org.feichtmeier.pulse.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pulse.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pulse"; @@ -509,7 +508,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.pulse.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = org.feichtmeier.pulse.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pulse.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pulse"; @@ -572,8 +571,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = Y7ZGTYFNR6; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -704,8 +705,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = Y7ZGTYFNR6; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -724,8 +727,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = Y7ZGTYFNR6; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index bb4c5c4..fcb0201 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -59,6 +59,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index d53ef64..b3c1761 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index ffc45cc..25da25a 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = pulse // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.pulse +PRODUCT_BUNDLE_IDENTIFIER = org.feichtmeier.pulse // The copyright displayed in application information PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index 62e521c..2569522 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -14,5 +14,7 @@ com.apple.security.network.client + keychain-access-groups + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 8fa9cdb..f9f0dc6 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -10,5 +10,7 @@ com.apple.security.network.client + keychain-access-groups + diff --git a/pubspec.lock b/pubspec.lock index ff79938..f16e96f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -53,42 +53,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: "direct main" description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" crypto: dependency: transitive description: @@ -117,18 +117,18 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" ffi: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" file: dependency: transitive description: @@ -271,7 +271,7 @@ packages: source: hosted version: "0.4.0" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f @@ -322,18 +322,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -362,10 +362,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -386,10 +386,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" open_weather_client: dependency: "direct main" description: @@ -402,10 +402,10 @@ packages: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_parsing: dependency: transitive description: @@ -466,10 +466,10 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.1.0" platform: dependency: transitive description: @@ -554,18 +554,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "688ee90fbfb6989c980254a56cb26ebe9bb30a3a2dff439a78894211f73de67a" + sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.5.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "650584dcc0a39856f369782874e562efd002a9c94aec032412c9eb81419cce1f" + sha256: ea86be7b7114f9e94fddfbb52649e59a03d6627ccd2387ebddcd6624719e9f16 url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.5" shared_preferences_foundation: dependency: transitive description: @@ -594,10 +594,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: @@ -615,18 +615,18 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" state_notifier: dependency: transitive description: @@ -639,34 +639,34 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" transparent_image: dependency: transitive description: @@ -695,10 +695,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" watch_it: dependency: "direct main" description: @@ -796,5 +796,5 @@ packages: source: hosted version: "0.0.3+1" sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.27.4" + dart: ">=3.7.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index 22ab91e..1cbf530 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ version: 1.0.0+1 environment: sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.27.4" + flutter: ">=3.29.0" dependencies: animated_emoji: ^3.1.0 @@ -25,6 +25,7 @@ dependencies: ref: 657a3107993c32d9aa85b272b2991440f3081f3a handy_window: ^0.4.0 + http: ^1.3.0 intl: ^0.19.0 open_weather_client: ^2.4.1 path: ^1.9.0 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index cd9bc63..5fa8a59 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -20,7 +20,7 @@ architectures: parts: flutter-git: source: https://github.com/flutter/flutter.git - source-tag: 3.27.4 + source-tag: 3.29.0 plugin: nil override-build: | mkdir -p $CRAFT_PART_INSTALL/usr/bin @@ -38,6 +38,9 @@ parts: prime: - -* +# TODO: How to get the api key from the environment of the github action? +# flutter build linux --release -v --dart-define=API_KEY="$API_KEY" + pulse: plugin: nil source: . @@ -47,8 +50,7 @@ parts: set -eu flutter doctor flutter pub get - export API_KEY=$API_KEY - flutter build linux --release -v --dart-define=API_KEY="$API_KEY" + flutter build linux --release -v mkdir -p $CRAFT_PART_INSTALL/bin/ cp -r build/linux/*/release/bundle/* $CRAFT_PART_INSTALL/bin/