[imp]: Refactor localization & convert screens to slivers

This commit is contained in:
nahnah
2026-02-04 22:12:34 +00:00
parent 781cabd7c8
commit 5467cb56a5
6 changed files with 1009 additions and 771 deletions
+321 -194
View File
@@ -1,306 +1,433 @@
import 'dart:convert';
import 'package:crowdin_sdk/crowdin_sdk.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'app_localizations.dart';
import 'package:crowdin_sdk/crowdin_sdk.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
class CrowdinLocalization extends AppLocalizations {
final AppLocalizations _fallbackTexts;
CrowdinLocalization(String locale, AppLocalizations fallbackTexts) : _fallbackTexts = fallbackTexts, super(locale);
static const LocalizationsDelegate<AppLocalizations> delegate = _CrowdinLocalizationsDelegate();
CrowdinLocalization(super.locale, AppLocalizations fallbackTexts)
: _fallbackTexts = fallbackTexts;
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <
LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
static const LocalizationsDelegate<AppLocalizations> delegate =
_CrowdinLocalizationsDelegate();
static const List<Locale> supportedLocales = AppLocalizations.supportedLocales;
@override
String get app_name => Crowdin.getText(localeName, 'app_name') ?? _fallbackTexts.app_name;
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
<LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
@override
String get welcome => Crowdin.getText(localeName, 'welcome') ?? _fallbackTexts.welcome;
static const List<Locale> supportedLocales =
AppLocalizations.supportedLocales;
@override
String get search => Crowdin.getText(localeName, 'search') ?? _fallbackTexts.search;
@override
String get app_name =>
Crowdin.getText(localeName, 'app_name') ?? _fallbackTexts.app_name;
@override
String get settings => Crowdin.getText(localeName, 'settings') ?? _fallbackTexts.settings;
@override
String get welcome =>
Crowdin.getText(localeName, 'welcome') ?? _fallbackTexts.welcome;
@override
@override
String get search =>
Crowdin.getText(localeName, 'search') ?? _fallbackTexts.search;
@override
String get settings =>
Crowdin.getText(localeName, 'settings') ?? _fallbackTexts.settings;
@override
String get home => Crowdin.getText(localeName, 'home') ?? _fallbackTexts.home;
@override
String get categories => Crowdin.getText(localeName, 'categories') ?? _fallbackTexts.categories;
@override
String get categories =>
Crowdin.getText(localeName, 'categories') ?? _fallbackTexts.categories;
@override
String get updates => Crowdin.getText(localeName, 'updates') ?? _fallbackTexts.updates;
@override
String get updates =>
Crowdin.getText(localeName, 'updates') ?? _fallbackTexts.updates;
@override
String get installed => Crowdin.getText(localeName, 'installed') ?? _fallbackTexts.installed;
@override
String get installed =>
Crowdin.getText(localeName, 'installed') ?? _fallbackTexts.installed;
@override
String get download => Crowdin.getText(localeName, 'download') ?? _fallbackTexts.download;
@override
String get download =>
Crowdin.getText(localeName, 'download') ?? _fallbackTexts.download;
@override
String get install => Crowdin.getText(localeName, 'install') ?? _fallbackTexts.install;
@override
String get install =>
Crowdin.getText(localeName, 'install') ?? _fallbackTexts.install;
@override
String get uninstall => Crowdin.getText(localeName, 'uninstall') ?? _fallbackTexts.uninstall;
@override
String get uninstall =>
Crowdin.getText(localeName, 'uninstall') ?? _fallbackTexts.uninstall;
@override
@override
String get open => Crowdin.getText(localeName, 'open') ?? _fallbackTexts.open;
@override
String get cancel => Crowdin.getText(localeName, 'cancel') ?? _fallbackTexts.cancel;
@override
String get cancel =>
Crowdin.getText(localeName, 'cancel') ?? _fallbackTexts.cancel;
@override
String get update_available => Crowdin.getText(localeName, 'update_available') ?? _fallbackTexts.update_available;
@override
String get update_available =>
Crowdin.getText(localeName, 'update_available') ??
_fallbackTexts.update_available;
@override
String get downloading => Crowdin.getText(localeName, 'downloading') ?? _fallbackTexts.downloading;
@override
String get downloading =>
Crowdin.getText(localeName, 'downloading') ?? _fallbackTexts.downloading;
@override
String get install_permission_required => Crowdin.getText(localeName, 'install_permission_required') ?? _fallbackTexts.install_permission_required;
@override
String get install_permission_required =>
Crowdin.getText(localeName, 'install_permission_required') ??
_fallbackTexts.install_permission_required;
@override
String get storage_permission_required => Crowdin.getText(localeName, 'storage_permission_required') ?? _fallbackTexts.storage_permission_required;
@override
String get storage_permission_required =>
Crowdin.getText(localeName, 'storage_permission_required') ??
_fallbackTexts.storage_permission_required;
@override
String get cancel_download => Crowdin.getText(localeName, 'cancel_download') ?? _fallbackTexts.cancel_download;
@override
String get cancel_download =>
Crowdin.getText(localeName, 'cancel_download') ??
_fallbackTexts.cancel_download;
@override
String get version => Crowdin.getText(localeName, 'version') ?? _fallbackTexts.version;
@override
String get version =>
Crowdin.getText(localeName, 'version') ?? _fallbackTexts.version;
@override
@override
String get size => Crowdin.getText(localeName, 'size') ?? _fallbackTexts.size;
@override
String get description => Crowdin.getText(localeName, 'description') ?? _fallbackTexts.description;
@override
String get description =>
Crowdin.getText(localeName, 'description') ?? _fallbackTexts.description;
@override
String get permissions => Crowdin.getText(localeName, 'permissions') ?? _fallbackTexts.permissions;
@override
String get permissions =>
Crowdin.getText(localeName, 'permissions') ?? _fallbackTexts.permissions;
@override
String get screenshots => Crowdin.getText(localeName, 'screenshots') ?? _fallbackTexts.screenshots;
@override
String get screenshots =>
Crowdin.getText(localeName, 'screenshots') ?? _fallbackTexts.screenshots;
@override
String get no_version_available => Crowdin.getText(localeName, 'no_version_available') ?? _fallbackTexts.no_version_available;
@override
String get no_version_available =>
Crowdin.getText(localeName, 'no_version_available') ??
_fallbackTexts.no_version_available;
@override
String get app_information => Crowdin.getText(localeName, 'app_information') ?? _fallbackTexts.app_information;
@override
String get app_information =>
Crowdin.getText(localeName, 'app_information') ??
_fallbackTexts.app_information;
@override
String get package_name => Crowdin.getText(localeName, 'package_name') ?? _fallbackTexts.package_name;
@override
String get package_name =>
Crowdin.getText(localeName, 'package_name') ??
_fallbackTexts.package_name;
@override
String get license => Crowdin.getText(localeName, 'license') ?? _fallbackTexts.license;
@override
String get license =>
Crowdin.getText(localeName, 'license') ?? _fallbackTexts.license;
@override
String get added => Crowdin.getText(localeName, 'added') ?? _fallbackTexts.added;
@override
String get added =>
Crowdin.getText(localeName, 'added') ?? _fallbackTexts.added;
@override
String get last_updated => Crowdin.getText(localeName, 'last_updated') ?? _fallbackTexts.last_updated;
@override
String get last_updated =>
Crowdin.getText(localeName, 'last_updated') ??
_fallbackTexts.last_updated;
@override
String get version_information => Crowdin.getText(localeName, 'version_information') ?? _fallbackTexts.version_information;
@override
String get version_information =>
Crowdin.getText(localeName, 'version_information') ??
_fallbackTexts.version_information;
@override
String get version_name => Crowdin.getText(localeName, 'version_name') ?? _fallbackTexts.version_name;
@override
String get version_name =>
Crowdin.getText(localeName, 'version_name') ??
_fallbackTexts.version_name;
@override
String get version_code => Crowdin.getText(localeName, 'version_code') ?? _fallbackTexts.version_code;
@override
String get version_code =>
Crowdin.getText(localeName, 'version_code') ??
_fallbackTexts.version_code;
@override
String get min_sdk => Crowdin.getText(localeName, 'min_sdk') ?? _fallbackTexts.min_sdk;
@override
String get min_sdk =>
Crowdin.getText(localeName, 'min_sdk') ?? _fallbackTexts.min_sdk;
@override
String get target_sdk => Crowdin.getText(localeName, 'target_sdk') ?? _fallbackTexts.target_sdk;
@override
String get target_sdk =>
Crowdin.getText(localeName, 'target_sdk') ?? _fallbackTexts.target_sdk;
@override
String get all_versions => Crowdin.getText(localeName, 'all_versions') ?? _fallbackTexts.all_versions;
@override
String get all_versions =>
Crowdin.getText(localeName, 'all_versions') ??
_fallbackTexts.all_versions;
@override
String get latest => Crowdin.getText(localeName, 'latest') ?? _fallbackTexts.latest;
@override
String get latest =>
Crowdin.getText(localeName, 'latest') ?? _fallbackTexts.latest;
@override
String get released => Crowdin.getText(localeName, 'released') ?? _fallbackTexts.released;
@override
String get released =>
Crowdin.getText(localeName, 'released') ?? _fallbackTexts.released;
@override
String get loading => Crowdin.getText(localeName, 'loading') ?? _fallbackTexts.loading;
@override
String get loading =>
Crowdin.getText(localeName, 'loading') ?? _fallbackTexts.loading;
@override
String get error => Crowdin.getText(localeName, 'error') ?? _fallbackTexts.error;
@override
String get error =>
Crowdin.getText(localeName, 'error') ?? _fallbackTexts.error;
@override
String get retry => Crowdin.getText(localeName, 'retry') ?? _fallbackTexts.retry;
@override
String get retry =>
Crowdin.getText(localeName, 'retry') ?? _fallbackTexts.retry;
@override
String get share => Crowdin.getText(localeName, 'share') ?? _fallbackTexts.share;
@override
String get share =>
Crowdin.getText(localeName, 'share') ?? _fallbackTexts.share;
@override
String get website => Crowdin.getText(localeName, 'website') ?? _fallbackTexts.website;
@override
String get website =>
Crowdin.getText(localeName, 'website') ?? _fallbackTexts.website;
@override
String get source_code => Crowdin.getText(localeName, 'source_code') ?? _fallbackTexts.source_code;
@override
String get source_code =>
Crowdin.getText(localeName, 'source_code') ?? _fallbackTexts.source_code;
@override
String get issue_tracker => Crowdin.getText(localeName, 'issue_tracker') ?? _fallbackTexts.issue_tracker;
@override
String get issue_tracker =>
Crowdin.getText(localeName, 'issue_tracker') ??
_fallbackTexts.issue_tracker;
@override
String get whats_new => Crowdin.getText(localeName, 'whats_new') ?? _fallbackTexts.whats_new;
@override
String get whats_new =>
Crowdin.getText(localeName, 'whats_new') ?? _fallbackTexts.whats_new;
@override
String get show_more => Crowdin.getText(localeName, 'show_more') ?? _fallbackTexts.show_more;
@override
String get show_more =>
Crowdin.getText(localeName, 'show_more') ?? _fallbackTexts.show_more;
@override
String get show_less => Crowdin.getText(localeName, 'show_less') ?? _fallbackTexts.show_less;
@override
String get show_less =>
Crowdin.getText(localeName, 'show_less') ?? _fallbackTexts.show_less;
@override
String get downloads_stats => Crowdin.getText(localeName, 'downloads_stats') ?? _fallbackTexts.downloads_stats;
@override
String get downloads_stats =>
Crowdin.getText(localeName, 'downloads_stats') ??
_fallbackTexts.downloads_stats;
@override
String get last_day => Crowdin.getText(localeName, 'last_day') ?? _fallbackTexts.last_day;
@override
String get last_day =>
Crowdin.getText(localeName, 'last_day') ?? _fallbackTexts.last_day;
@override
String get last_30_days => Crowdin.getText(localeName, 'last_30_days') ?? _fallbackTexts.last_30_days;
@override
String get last_30_days =>
Crowdin.getText(localeName, 'last_30_days') ??
_fallbackTexts.last_30_days;
@override
String get last_365_days => Crowdin.getText(localeName, 'last_365_days') ?? _fallbackTexts.last_365_days;
@override
String get last_365_days =>
Crowdin.getText(localeName, 'last_365_days') ??
_fallbackTexts.last_365_days;
@override
String get not_available => Crowdin.getText(localeName, 'not_available') ?? _fallbackTexts.not_available;
@override
String get not_available =>
Crowdin.getText(localeName, 'not_available') ??
_fallbackTexts.not_available;
@override
String get download_failed => Crowdin.getText(localeName, 'download_failed') ?? _fallbackTexts.download_failed;
@override
String get download_failed =>
Crowdin.getText(localeName, 'download_failed') ??
_fallbackTexts.download_failed;
@override
String get installation_failed => Crowdin.getText(localeName, 'installation_failed') ?? _fallbackTexts.installation_failed;
@override
String get installation_failed =>
Crowdin.getText(localeName, 'installation_failed') ??
_fallbackTexts.installation_failed;
@override
String get uninstall_failed => Crowdin.getText(localeName, 'uninstall_failed') ?? _fallbackTexts.uninstall_failed;
@override
String get uninstall_failed =>
Crowdin.getText(localeName, 'uninstall_failed') ??
_fallbackTexts.uninstall_failed;
@override
String get open_failed => Crowdin.getText(localeName, 'open_failed') ?? _fallbackTexts.open_failed;
@override
String get open_failed =>
Crowdin.getText(localeName, 'open_failed') ?? _fallbackTexts.open_failed;
@override
String get device => Crowdin.getText(localeName, 'device') ?? _fallbackTexts.device;
@override
String get device =>
Crowdin.getText(localeName, 'device') ?? _fallbackTexts.device;
@override
String get recently_updated => Crowdin.getText(localeName, 'recently_updated') ?? _fallbackTexts.recently_updated;
@override
String get recently_updated =>
Crowdin.getText(localeName, 'recently_updated') ??
_fallbackTexts.recently_updated;
@override
String get refresh => Crowdin.getText(localeName, 'refresh') ?? _fallbackTexts.refresh;
@override
String get refresh =>
Crowdin.getText(localeName, 'refresh') ?? _fallbackTexts.refresh;
@override
String get about => Crowdin.getText(localeName, 'about') ?? _fallbackTexts.about;
@override
String get about =>
Crowdin.getText(localeName, 'about') ?? _fallbackTexts.about;
@override
String get refreshing_data => Crowdin.getText(localeName, 'refreshing_data') ?? _fallbackTexts.refreshing_data;
@override
String get refreshing_data =>
Crowdin.getText(localeName, 'refreshing_data') ??
_fallbackTexts.refreshing_data;
@override
String get data_refreshed => Crowdin.getText(localeName, 'data_refreshed') ?? _fallbackTexts.data_refreshed;
@override
String get data_refreshed =>
Crowdin.getText(localeName, 'data_refreshed') ??
_fallbackTexts.data_refreshed;
@override
String get refresh_failed => Crowdin.getText(localeName, 'refresh_failed') ?? _fallbackTexts.refresh_failed;
@override
String get refresh_failed =>
Crowdin.getText(localeName, 'refresh_failed') ??
_fallbackTexts.refresh_failed;
@override
String get loading_latest_apps => Crowdin.getText(localeName, 'loading_latest_apps') ?? _fallbackTexts.loading_latest_apps;
@override
String get loading_latest_apps =>
Crowdin.getText(localeName, 'loading_latest_apps') ??
_fallbackTexts.loading_latest_apps;
@override
String get latest_apps => Crowdin.getText(localeName, 'latest_apps') ?? _fallbackTexts.latest_apps;
@override
String get latest_apps =>
Crowdin.getText(localeName, 'latest_apps') ?? _fallbackTexts.latest_apps;
@override
String get no_apps_found => Crowdin.getText(localeName, 'no_apps_found') ?? _fallbackTexts.no_apps_found;
@override
String get no_apps_found =>
Crowdin.getText(localeName, 'no_apps_found') ??
_fallbackTexts.no_apps_found;
@override
String get searching => Crowdin.getText(localeName, 'searching') ?? _fallbackTexts.searching;
@override
String get searching =>
Crowdin.getText(localeName, 'searching') ?? _fallbackTexts.searching;
@override
String get setup_failed => Crowdin.getText(localeName, 'setup_failed') ?? _fallbackTexts.setup_failed;
@override
String get setup_failed =>
Crowdin.getText(localeName, 'setup_failed') ??
_fallbackTexts.setup_failed;
@override
@override
String get back => Crowdin.getText(localeName, 'back') ?? _fallbackTexts.back;
@override
String get allow => Crowdin.getText(localeName, 'allow') ?? _fallbackTexts.allow;
@override
String get allow =>
Crowdin.getText(localeName, 'allow') ?? _fallbackTexts.allow;
@override
String get manage_repositories => Crowdin.getText(localeName, 'manage_repositories') ?? _fallbackTexts.manage_repositories;
@override
String get manage_repositories =>
Crowdin.getText(localeName, 'manage_repositories') ??
_fallbackTexts.manage_repositories;
@override
String get enable_disable => Crowdin.getText(localeName, 'enable_disable') ?? _fallbackTexts.enable_disable;
@override
String get enable_disable =>
Crowdin.getText(localeName, 'enable_disable') ??
_fallbackTexts.enable_disable;
@override
@override
String get edit => Crowdin.getText(localeName, 'edit') ?? _fallbackTexts.edit;
@override
String get delete => Crowdin.getText(localeName, 'delete') ?? _fallbackTexts.delete;
@override
String get delete =>
Crowdin.getText(localeName, 'delete') ?? _fallbackTexts.delete;
@override
String get delete_repository => Crowdin.getText(localeName, 'delete_repository') ?? _fallbackTexts.delete_repository;
@override
String get delete_repository =>
Crowdin.getText(localeName, 'delete_repository') ??
_fallbackTexts.delete_repository;
@override
String delete_repository_confirm(Object name, Object repo) => Crowdin.getText(localeName, 'delete_repository_confirm', {'name': name, 'repo': repo}) ?? _fallbackTexts.delete_repository_confirm(name, repo);
@override
String delete_repository_confirm(Object name, Object repo) =>
Crowdin.getText(localeName, 'delete_repository_confirm', {
'name': name,
'repo': repo,
}) ??
_fallbackTexts.delete_repository_confirm(name, repo);
@override
String get updating_repository => Crowdin.getText(localeName, 'updating_repository') ?? _fallbackTexts.updating_repository;
@override
String get updating_repository =>
Crowdin.getText(localeName, 'updating_repository') ??
_fallbackTexts.updating_repository;
@override
String get touch_grass_message => Crowdin.getText(localeName, 'touch_grass_message') ?? _fallbackTexts.touch_grass_message;
@override
String get touch_grass_message =>
Crowdin.getText(localeName, 'touch_grass_message') ??
_fallbackTexts.touch_grass_message;
@override
String get add_repository => Crowdin.getText(localeName, 'add_repository') ?? _fallbackTexts.add_repository;
@override
String get add_repository =>
Crowdin.getText(localeName, 'add_repository') ??
_fallbackTexts.add_repository;
@override
@override
String get add => Crowdin.getText(localeName, 'add') ?? _fallbackTexts.add;
@override
@override
String get save => Crowdin.getText(localeName, 'save') ?? _fallbackTexts.save;
@override
String get enter_repository_name => Crowdin.getText(localeName, 'enter_repository_name') ?? _fallbackTexts.enter_repository_name;
@override
String get enter_repository_name =>
Crowdin.getText(localeName, 'enter_repository_name') ??
_fallbackTexts.enter_repository_name;
@override
String get enter_repository_url => Crowdin.getText(localeName, 'enter_repository_url') ?? _fallbackTexts.enter_repository_url;
@override
String get enter_repository_url =>
Crowdin.getText(localeName, 'enter_repository_url') ??
_fallbackTexts.enter_repository_url;
@override
String get edit_repository => Crowdin.getText(localeName, 'edit_repository') ?? _fallbackTexts.edit_repository;
@override
String get edit_repository =>
Crowdin.getText(localeName, 'edit_repository') ??
_fallbackTexts.edit_repository;
@override
String get loading_apps => Crowdin.getText(localeName, 'loading_apps') ?? _fallbackTexts.loading_apps;
@override
String get loading_apps =>
Crowdin.getText(localeName, 'loading_apps') ??
_fallbackTexts.loading_apps;
@override
String no_apps_in_category(Object category) => Crowdin.getText(localeName, 'no_apps_in_category', {'category':category}) ?? _fallbackTexts.no_apps_in_category(category);
@override
String no_apps_in_category(Object category) =>
Crowdin.getText(localeName, 'no_apps_in_category', {
'category': category,
}) ??
_fallbackTexts.no_apps_in_category(category);
@override
String get loading_categories => Crowdin.getText(localeName, 'loading_categories') ?? _fallbackTexts.loading_categories;
@override
String get no_categories_found => Crowdin.getText(localeName, 'no_categories_found') ?? _fallbackTexts.no_categories_found;
@override
String get loading_categories =>
Crowdin.getText(localeName, 'loading_categories') ??
_fallbackTexts.loading_categories;
@override
String get no_categories_found =>
Crowdin.getText(localeName, 'no_categories_found') ??
_fallbackTexts.no_categories_found;
}
class _CrowdinLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
class _CrowdinLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _CrowdinLocalizationsDelegate();
@override
Future<AppLocalizations> load(Locale locale) =>
AppLocalizations.delegate.load(locale)
.then((fallback) => CrowdinLocalization(locale.toString(), fallback));
Future<AppLocalizations> load(Locale locale) => AppLocalizations.delegate
.load(locale)
.then((fallback) => CrowdinLocalization(locale.toString(), fallback));
@override
bool isSupported(Locale locale) => AppLocalizations.supportedLocales.contains(locale);
bool isSupported(Locale locale) =>
AppLocalizations.supportedLocales.contains(locale);
@override
bool shouldReload(_CrowdinLocalizationsDelegate old) => false;
+3 -2
View File
@@ -269,7 +269,7 @@ class _AppDetailsScreenState extends State<AppDetailsScreen> {
if (availableRepos == null || availableRepos.isEmpty) return;
// Capture the mounted context before showing dialog
final scaffoldMessenger = ScaffoldMessenger.of(context);
// final scaffoldMessenger = ScaffoldMessenger.of(context);
await showModalBottomSheet(
context: context,
@@ -370,7 +370,7 @@ class _AppDetailsScreenState extends State<AppDetailsScreen> {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar.large(
SliverAppBar(
pinned: true,
centerTitle: false,
title: Row(
@@ -428,6 +428,7 @@ class _AppDetailsScreenState extends State<AppDetailsScreen> {
},
),
PopupMenuButton<String>(
icon: const Icon(Symbols.more_vert),
onSelected: (value) async {
switch (value) {
case 'website':
+65 -60
View File
@@ -1,4 +1,3 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:florid/l10n/app_localizations.dart';
import 'package:florid/providers/app_provider.dart';
import 'package:florid/providers/repositories_provider.dart';
@@ -7,7 +6,7 @@ import 'package:florid/screens/home_screen.dart';
import 'package:florid/utils/menu_actions.dart';
import 'package:florid/widgets/f_tabbar.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
class LibraryScreen extends StatefulWidget {
@@ -37,67 +36,73 @@ class _LibraryScreenState extends State<LibraryScreen>
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.app_name),
backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow,
surfaceTintColor: Theme.of(context).colorScheme.surfaceContainerLow,
actions: [
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case 'refresh':
_refreshData();
break;
case 'settings':
MenuActions.showSettings(context);
break;
case 'about':
MenuActions.showAbout(context);
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'refresh',
child: ListTile(
leading: const Icon(Symbols.refresh),
title: Text(AppLocalizations.of(context)!.refresh),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'settings',
child: ListTile(
leading: const Icon(Symbols.settings),
title: Text(AppLocalizations.of(context)!.settings),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'about',
child: ListTile(
leading: Icon(Symbols.info),
title: Text(AppLocalizations.of(context)!.about),
contentPadding: EdgeInsets.zero,
),
body: CustomScrollView(
slivers: [
SliverAppBar(
title: Text(AppLocalizations.of(context)!.app_name),
backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow,
surfaceTintColor: Theme.of(context).colorScheme.surfaceContainerLow,
actions: [
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case 'refresh':
_refreshData();
break;
case 'settings':
MenuActions.showSettings(context);
break;
case 'about':
MenuActions.showAbout(context);
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'refresh',
child: ListTile(
leading: const Icon(Symbols.refresh),
title: Text(AppLocalizations.of(context)!.refresh),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'settings',
child: ListTile(
leading: const Icon(Symbols.settings),
title: Text(AppLocalizations.of(context)!.settings),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'about',
child: ListTile(
leading: Icon(Symbols.info),
title: Text(AppLocalizations.of(context)!.about),
contentPadding: EdgeInsets.zero,
),
),
],
),
],
bottom: FTabBar(
controller: _tabController,
onTabChanged: (index) {
_tabController.animateTo(index);
},
items: [
FloridTabBarItem(
icon: Symbols.home,
label: AppLocalizations.of(context)!.home,
),
FloridTabBarItem(
icon: Symbols.category,
label: AppLocalizations.of(context)!.categories,
),
],
),
),
],
bottom: FTabBar(
controller: _tabController,
onTabChanged: (index) {
_tabController.animateTo(index);
},
items: [
FloridTabBarItem(icon: Symbols.home, label: AppLocalizations.of(context)!.home),
FloridTabBarItem(icon: Symbols.category, label: AppLocalizations.of(context)!.categories),
],
),
),
body: Column(
children: [
Expanded(
SliverFillRemaining(
child: TabBarView(controller: _tabController, children: tabs),
),
],
+283 -253
View File
@@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:florid/l10n/app_localizations.dart';
import 'package:florid/widgets/m_list.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
@@ -56,279 +57,308 @@ class _RepositoriesScreenState extends State<RepositoriesScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Manage Repositories')),
body: Consumer<RepositoriesProvider>(
builder: (context, provider, _) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
body: CustomScrollView(
slivers: [
SliverAppBar.medium(
leading: BackButton(
style: ButtonStyle(
backgroundColor: WidgetStateColor.resolveWith(
(states) => Theme.of(context).colorScheme.surfaceContainer,
),
),
),
title: Text(AppLocalizations.of(context)!.manage_repositories),
),
SliverToBoxAdapter(
child: Consumer<RepositoriesProvider>(
builder: (context, provider, _) {
if (provider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Column(
spacing: 16,
children: [
// Error message if any
if (provider.error != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
color: Colors.red.shade100,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: [
Icon(Symbols.error, color: Colors.red.shade700),
const SizedBox(width: 12),
Expanded(
child: Text(
provider.error ?? 'Unknown error',
style: TextStyle(color: Colors.red.shade700),
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Column(
spacing: 16,
children: [
// Error message if any
if (provider.error != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
color: Colors.red.shade100,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: [
Icon(
Symbols.error,
color: Colors.red.shade700,
),
const SizedBox(width: 12),
Expanded(
child: Text(
provider.error ?? 'Unknown error',
style: TextStyle(
color: Colors.red.shade700,
),
),
),
IconButton(
icon: const Icon(Symbols.close),
onPressed: provider.clearError,
color: Colors.red.shade700,
),
],
),
),
IconButton(
icon: const Icon(Symbols.close),
onPressed: provider.clearError,
color: Colors.red.shade700,
),
),
// Presets section
if (_presets.isNotEmpty)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.0,
children: [
MListHeader(title: 'Preset'),
MListViewBuilder(
itemCount: _presets.length,
itemBuilder: (index) {
final preset = _presets[index];
final isAdded = provider.repositories.any(
(repo) => repo.url == preset['url'],
);
return MListItemData(
title: preset['name']!,
subtitle: preset['description']!,
onTap: () {},
suffix: Switch(
value: isAdded,
onChanged: (newValue) async {
if (newValue) {
// Add the preset
final repoProvider = context
.read<RepositoriesProvider>();
await repoProvider.addRepository(
preset['name']!,
preset['url']!,
);
// Only proceed with modal and refresh if addition succeeded (no error)
if (repoProvider.error == null &&
context.mounted) {
await _runRepositoryActionWithDialog(
context,
() async {
final apiService = context
.read<FDroidApiService>();
final appProvider = context
.read<AppProvider>();
await apiService
.clearRepositoryCache();
await appProvider.refreshAll(
repositoriesProvider:
repoProvider,
);
},
);
}
} else {
// Remove the preset
Repository? addedRepo;
try {
addedRepo = provider.repositories
.firstWhere(
(repo) =>
repo.url == preset['url'],
);
} catch (e) {
addedRepo = null;
}
if (addedRepo != null) {
final repoProvider = context
.read<RepositoriesProvider>();
repoProvider.deleteRepository(
addedRepo.id,
);
// Only proceed with modal and refresh if deletion succeeded (no error)
if (repoProvider.error == null &&
context.mounted) {
await _runRepositoryActionWithDialog(
context,
() async {
final apiService = context
.read<FDroidApiService>();
final appProvider = context
.read<AppProvider>();
await apiService
.clearRepositoryCache();
await appProvider.refreshAll(
repositoriesProvider:
repoProvider,
);
},
);
}
}
}
},
),
);
},
),
],
),
),
),
),
// Presets section
if (_presets.isNotEmpty)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.0,
children: [
MListHeader(title: 'Preset'),
MListViewBuilder(
itemCount: _presets.length,
itemBuilder: (index) {
final preset = _presets[index];
final isAdded = provider.repositories.any(
(repo) => repo.url == preset['url'],
);
return MListItemData(
title: preset['name']!,
subtitle: preset['description']!,
onTap: () {},
suffix: Switch(
value: isAdded,
onChanged: (newValue) async {
if (newValue) {
// Add the preset
final repoProvider = context
.read<RepositoriesProvider>();
await repoProvider.addRepository(
preset['name']!,
preset['url']!,
);
// Only proceed with modal and refresh if addition succeeded (no error)
if (repoProvider.error == null &&
context.mounted) {
await _runRepositoryActionWithDialog(
context,
() async {
final apiService = context
.read<FDroidApiService>();
final appProvider = context
.read<AppProvider>();
await apiService.clearRepositoryCache();
await appProvider.refreshAll(
repositoriesProvider: repoProvider,
);
},
);
}
} else {
// Remove the preset
Repository? addedRepo;
try {
addedRepo = provider.repositories
.firstWhere(
(repo) => repo.url == preset['url'],
);
} catch (e) {
addedRepo = null;
}
if (addedRepo != null) {
final repoProvider = context
.read<RepositoriesProvider>();
repoProvider.deleteRepository(addedRepo.id);
// Only proceed with modal and refresh if deletion succeeded (no error)
if (repoProvider.error == null &&
context.mounted) {
await _runRepositoryActionWithDialog(
context,
() async {
final apiService = context
.read<FDroidApiService>();
final appProvider = context
.read<AppProvider>();
await apiService
.clearRepositoryCache();
await appProvider.refreshAll(
repositoriesProvider: repoProvider,
);
},
);
}
}
}
},
),
);
},
),
],
),
// Custom Repositories list
Column(
spacing: 4.0,
children: [
MListHeader(title: 'Your Repositories'),
provider.repositories
.where(
(repo) => !_presets.any(
(preset) => preset['url'] == repo.url,
),
)
.toList()
.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Symbols.inbox,
size: 64,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
'No custom repositories added',
style: Theme.of(
context,
).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
'Add a custom F-Droid repository to get started',
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.center,
),
],
),
)
: MListViewBuilder(
itemCount: provider.repositories
.where(
(repo) => !_presets.any(
(preset) => preset['url'] == repo.url,
),
)
.length,
itemBuilder: (index) {
final customRepos = provider.repositories
// Custom Repositories list
Column(
spacing: 4.0,
children: [
MListHeader(title: 'Your Repositories'),
provider.repositories
.where(
(repo) => !_presets.any(
(preset) => preset['url'] == repo.url,
),
)
.toList();
final repo = customRepos[index];
return MListItemData(
title: repo.name,
subtitle: repo.url,
suffix: Row(
mainAxisSize: MainAxisSize.min,
children: [
Switch(
value: repo.isEnabled,
onChanged: (_) async {
await _toggleRepositoryWithDialog(
.toList()
.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Symbols.inbox,
size: 64,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
'No custom repositories added',
style: Theme.of(
context,
provider,
repo.id,
);
},
),
PopupMenuButton<String>(
onSelected: (value) async {
switch (value) {
case 'edit':
_RepositoryListItem(
repository: repo,
)._showEditRepositoryDialog(
context,
repo,
);
break;
case 'delete':
_RepositoryListItem(
repository: repo,
)._showDeleteConfirmation(
context,
repo,
provider,
);
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'edit',
child: Row(
spacing: 16.0,
children: [
Icon(Symbols.edit_rounded),
Text('Edit'),
],
),
).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
'Add a custom F-Droid repository to get started',
style: Theme.of(
context,
).textTheme.bodySmall,
textAlign: TextAlign.center,
),
],
),
)
: MListViewBuilder(
itemCount: provider.repositories
.where(
(repo) => !_presets.any(
(preset) => preset['url'] == repo.url,
),
PopupMenuItem(
value: 'delete',
child: Row(
spacing: 16.0,
children: [
Icon(
Symbols.delete_rounded,
fill: 1,
color: Theme.of(
context,
).colorScheme.error,
),
Text(
'Delete',
style: TextStyle(
color: Theme.of(
)
.length,
itemBuilder: (index) {
final customRepos = provider.repositories
.where(
(repo) => !_presets.any(
(preset) =>
preset['url'] == repo.url,
),
)
.toList();
final repo = customRepos[index];
return MListItemData(
title: repo.name,
subtitle: repo.url,
suffix: Row(
mainAxisSize: MainAxisSize.min,
children: [
Switch(
value: repo.isEnabled,
onChanged: (_) async {
await _toggleRepositoryWithDialog(
context,
provider,
repo.id,
);
},
),
PopupMenuButton<String>(
onSelected: (value) async {
switch (value) {
case 'edit':
_RepositoryListItem(
repository: repo,
)._showEditRepositoryDialog(
context,
).colorScheme.error,
repo,
);
break;
case 'delete':
_RepositoryListItem(
repository: repo,
)._showDeleteConfirmation(
context,
repo,
provider,
);
break;
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'edit',
child: Row(
spacing: 16.0,
children: [
Icon(Symbols.edit_rounded),
Text('Edit'),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
spacing: 16.0,
children: [
Icon(
Symbols.delete_rounded,
fill: 1,
color: Theme.of(
context,
).colorScheme.error,
),
Text(
'Delete',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.error,
),
),
],
),
),
],
),
),
],
),
],
],
),
onTap: () {},
);
},
),
onTap: () {},
);
},
),
],
),
],
],
),
],
),
);
},
),
);
},
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => _showAddRepositoryDialog(context),
+259 -255
View File
@@ -75,267 +75,271 @@ class _SettingsScreenState extends State<SettingsScreen> {
return Consumer<SettingsProvider>(
builder: (context, settings, _) {
return Scaffold(
appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Column(
spacing: 4,
children: [
MListHeader(title: 'Theme Mode'),
MRadioListView(
items: [
MRadioListItemData<ThemeMode>(
title: 'Follow system theme',
subtitle: '',
value: ThemeMode.system,
),
MRadioListItemData<ThemeMode>(
title: 'Light theme',
subtitle: '',
value: ThemeMode.light,
),
MRadioListItemData<ThemeMode>(
title: 'Dark theme',
subtitle: '',
value: ThemeMode.dark,
),
],
groupValue: settings.themeMode,
onChanged: (mode) {
settings.setThemeMode(mode);
},
),
MListHeader(title: 'Theme Style'),
MRadioListView(
items: [
MRadioListItemData<ThemeStyle>(
title: 'Material style',
subtitle: '',
value: ThemeStyle.material,
),
MRadioListItemData<ThemeStyle>(
title: 'Florid style',
subtitle: '',
suffix: Container(
margin: const EdgeInsets.only(right: 8.0),
child: Material(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(99.0),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 2.0,
),
child: Text(
'Beta',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSecondary,
),
body: CustomScrollView(
slivers: [
SliverAppBar.medium(
title: Text(AppLocalizations.of(context)!.settings),
),
SliverToBoxAdapter(
child: Column(
spacing: 4,
children: [
MListHeader(title: 'Theme Mode'),
MRadioListView(
items: [
MRadioListItemData<ThemeMode>(
title: 'Follow system theme',
subtitle: '',
value: ThemeMode.system,
),
MRadioListItemData<ThemeMode>(
title: 'Light theme',
subtitle: '',
value: ThemeMode.light,
),
MRadioListItemData<ThemeMode>(
title: 'Dark theme',
subtitle: '',
value: ThemeMode.dark,
),
],
groupValue: settings.themeMode,
onChanged: (mode) {
settings.setThemeMode(mode);
},
),
MListHeader(title: 'Theme Style'),
MRadioListView(
items: [
MRadioListItemData<ThemeStyle>(
title: 'Material style',
subtitle: '',
value: ThemeStyle.material,
),
MRadioListItemData<ThemeStyle>(
title: 'Florid style',
subtitle: '',
suffix: Container(
margin: const EdgeInsets.only(right: 8.0),
child: Material(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(99.0),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 2.0,
),
child: Text(
'Beta',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSecondary,
),
),
),
),
value: ThemeStyle.florid,
),
],
groupValue: settings.themeStyle,
onChanged: (style) {
settings.setThemeStyle(style);
},
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.feedback),
title: 'Fedback on Florid theme',
subtitle:
'Help improve the Florid theme by providing feedback',
onTap: () {
launchUrl(
Uri.parse(
'https://github.com/Nandanrmenon/florid/discussions/5',
),
);
},
suffix: Icon(Symbols.open_in_new),
),
],
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'General Settings'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.language),
title: 'App content language',
onTap: () => _showLanguageDialog(context, settings),
subtitle: SettingsProvider.getLocaleDisplayName(
settings.locale,
),
suffix: Icon(Symbols.chevron_right),
),
MListItemData(
leading: Icon(Symbols.cloud),
title: 'Manage repositories',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const RepositoriesScreen(),
),
);
},
subtitle: 'Add or remove F-Droid repositories',
suffix: Icon(Symbols.chevron_right),
),
],
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'Downloads & Storage'),
MListView(
items: [
MListItemData(
title: 'Auto-install after download',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Install APKs automatically once download finishes',
suffix: Switch(
value: settings.autoInstallApk,
onChanged: (value) {
settings.setAutoInstallApk(value);
},
),
),
MListItemData(
title: 'Delete APK after install',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Remove installer files after successful installation',
suffix: Switch(
value: settings.autoDeleteApk,
onChanged: (value) {
settings.setAutoDeleteApk(value);
},
),
),
],
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.cleaning_services),
title: 'Clear repository cache',
onTap: () {
_clearRepoCache(context);
},
subtitle:
'Refresh app list and metadata on next load',
),
MListItemData(
leading: Icon(Symbols.delete_sweep),
title: 'Clear APK downloads',
onTap: () {
_clearRepoCache(context);
},
subtitle:
'Remove downloaded installer files from storage',
),
MListItemData(
leading: Icon(Symbols.image_not_supported),
title: 'Clear image cache',
onTap: () {
_clearRepoCache(context);
},
subtitle: 'Remove cached icons and screenshots',
),
],
),
],
),
Column(
spacing: 4,
children: [
MListHeader(title: 'About'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.info),
title: 'Version',
subtitle: _appVersion.isEmpty
? 'Loading…'
: _appVersion,
onTap: () {},
),
MListItemData(
leading: Icon(Symbols.code_rounded),
title: 'Source code',
onTap: () async {
final url = Uri.parse(
'https://github.com/Nandanrmenon/florid',
);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
},
),
MListItemData(
leading: Icon(Symbols.bug_report_rounded),
title: 'Report an issue',
onTap: () async {
final url = Uri.parse(
'https://github.com/Nandanrmenon/florid/issues/new?template=bug_report.md',
);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
},
),
MListItemData(
leading: Icon(Symbols.share),
title: 'Share Florid',
onTap: () {
SharePlus.instance.share(
ShareParams(
title: 'Check out Florid!',
text:
'A modern F-Droid client! https://github.com/Nandanrmenon/florid',
),
);
},
),
],
),
],
),
],
value: ThemeStyle.florid,
),
],
groupValue: settings.themeStyle,
onChanged: (style) {
settings.setThemeStyle(style);
},
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.feedback),
title: 'Fedback on Florid theme',
subtitle:
'Help improve the Florid theme by providing feedback',
onTap: () {
launchUrl(
Uri.parse(
'https://github.com/Nandanrmenon/florid/discussions/5',
),
);
},
suffix: Icon(Symbols.open_in_new),
),
],
),
],
),
),
),
SliverToBoxAdapter(
child: Column(
spacing: 4,
children: [
MListHeader(title: 'General Settings'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.language),
title: 'App content language',
onTap: () => _showLanguageDialog(context, settings),
subtitle: SettingsProvider.getLocaleDisplayName(
settings.locale,
),
suffix: Icon(Symbols.chevron_right),
),
MListItemData(
leading: Icon(Symbols.cloud),
title: 'Manage repositories',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const RepositoriesScreen(),
),
);
},
subtitle: 'Add or remove F-Droid repositories',
suffix: Icon(Symbols.chevron_right),
),
],
),
],
),
),
SliverToBoxAdapter(
child: Column(
spacing: 4,
children: [
MListHeader(title: 'Downloads & Storage'),
MListView(
items: [
MListItemData(
title: 'Auto-install after download',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Install APKs automatically once download finishes',
suffix: Switch(
value: settings.autoInstallApk,
onChanged: (value) {
settings.setAutoInstallApk(value);
},
),
),
MListItemData(
title: 'Delete APK after install',
onTap: () {
settings.setAutoInstallApk(
!settings.autoInstallApk,
);
},
subtitle:
'Remove installer files after successful installation',
suffix: Switch(
value: settings.autoDeleteApk,
onChanged: (value) {
settings.setAutoDeleteApk(value);
},
),
),
],
),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.cleaning_services),
title: 'Clear repository cache',
onTap: () {
_clearRepoCache(context);
},
subtitle:
'Refresh app list and metadata on next load',
),
MListItemData(
leading: Icon(Symbols.delete_sweep),
title: 'Clear APK downloads',
onTap: () {
_clearRepoCache(context);
},
subtitle:
'Remove downloaded installer files from storage',
),
MListItemData(
leading: Icon(Symbols.image_not_supported),
title: 'Clear image cache',
onTap: () {
_clearRepoCache(context);
},
subtitle: 'Remove cached icons and screenshots',
),
],
),
],
),
),
SliverToBoxAdapter(
child: Column(
spacing: 4,
children: [
MListHeader(title: 'About'),
MListView(
items: [
MListItemData(
leading: Icon(Symbols.info),
title: 'Version',
subtitle: _appVersion.isEmpty
? 'Loading…'
: _appVersion,
onTap: () {},
),
MListItemData(
leading: Icon(Symbols.code_rounded),
title: 'Source code',
onTap: () async {
final url = Uri.parse(
'https://github.com/Nandanrmenon/florid',
);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
},
),
MListItemData(
leading: Icon(Symbols.bug_report_rounded),
title: 'Report an issue',
onTap: () async {
final url = Uri.parse(
'https://github.com/Nandanrmenon/florid/issues/new?template=bug_report.md',
);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
},
),
MListItemData(
leading: Icon(Symbols.share),
title: 'Share Florid',
onTap: () {
SharePlus.instance.share(
ShareParams(
title: 'Check out Florid!',
text:
'A modern F-Droid client! https://github.com/Nandanrmenon/florid',
),
);
},
),
],
),
],
),
),
SliverToBoxAdapter(child: const SizedBox(height: 32)),
],
),
);
},
+78 -7
View File
@@ -1,5 +1,6 @@
import 'package:florid/constants.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
class AppThemes {
// Material Theme (Original)
@@ -91,6 +92,15 @@ class AppThemes {
brightness: Brightness.light,
).onSurface,
),
backgroundColor: ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).surface,
surfaceTintColor: ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).surfaceContainerHigh,
elevation: 0,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
elevation: 0,
@@ -140,7 +150,29 @@ class AppThemes {
),
),
),
iconButtonTheme: IconButtonThemeData(
style: IconButton.styleFrom(
backgroundColor: ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).surfaceContainerLow,
),
),
switchTheme: SwitchThemeData(
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return Icon(
Symbols.check,
color: ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).onPrimary,
);
}
return const Icon(Symbols.close);
}),
thumbColor: WidgetStateProperty.resolveWith<Color?>((
Set<WidgetState> states,
) {
@@ -148,7 +180,7 @@ class AppThemes {
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).primaryContainer;
).primary;
}
return null; // Use the default thumb color
}),
@@ -159,14 +191,26 @@ class AppThemes {
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).secondary;
).surface;
}
return null; // Use the default track color
// Change the default track color to a custom color
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).surfaceContainerLowest;
}),
trackOutlineWidth: WidgetStateProperty.resolveWith<double?>((
Set<WidgetState> states,
) {
return 1;
return 2;
}),
trackOutlineColor: WidgetStateProperty.resolveWith<Color?>((
Set<WidgetState> states,
) {
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.light,
).surfaceContainerHighest;
}),
),
navigationBarTheme: NavigationBarThemeData(
@@ -283,6 +327,14 @@ class AppThemes {
brightness: Brightness.dark,
).onSurface,
),
backgroundColor: ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.dark,
).surface,
surfaceTintColor: ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.dark,
).surfaceContainerHigh,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
elevation: 0,
@@ -336,6 +388,14 @@ class AppThemes {
),
),
switchTheme: SwitchThemeData(
thumbIcon: WidgetStateProperty.resolveWith<Icon?>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return const Icon(Symbols.check);
}
return const Icon(Symbols.close);
}),
thumbColor: WidgetStateProperty.resolveWith<Color?>((
Set<WidgetState> states,
) {
@@ -354,14 +414,25 @@ class AppThemes {
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.dark,
).secondary;
).surface;
}
return null; // Use the default track color
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.dark,
).surfaceContainerLowest;
}),
trackOutlineWidth: WidgetStateProperty.resolveWith<double?>((
Set<WidgetState> states,
) {
return 1;
return 2;
}),
trackOutlineColor: WidgetStateProperty.resolveWith<Color?>((
Set<WidgetState> states,
) {
return ColorScheme.fromSeed(
seedColor: kAppColor,
brightness: Brightness.dark,
).surfaceContainerHighest;
}),
),
navigationBarTheme: NavigationBarThemeData(