From 5041a30fb5f4efb713cb0de3a275b38bab96be18 Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Wed, 24 Aug 2022 13:22:36 +0100 Subject: [PATCH] feat: add Sliver App Bar to Home, Patcher, Installer and Settings views --- assets/i18n/en.json | 4 +- lib/ui/views/home/home_view.dart | 167 +++++----- lib/ui/views/installer/installer_view.dart | 294 ++++++++++-------- .../views/installer/installer_viewmodel.dart | 2 +- lib/ui/views/patcher/patcher_view.dart | 77 +++-- lib/ui/views/settings/settings_view.dart | 264 ++++++++-------- 6 files changed, 444 insertions(+), 364 deletions(-) diff --git a/assets/i18n/en.json b/assets/i18n/en.json index f5662ae1..a543719c 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -59,8 +59,8 @@ }, "installerView": { "widgetTitle": "Installer", - "fabInstallButton": "Install", - "fabOpenButton": "Open", + "installButton": "Install", + "openButton": "Open", "notificationTitle": "ReVanced Manager is patching", "notificationText": "Tap to return to the installer" }, diff --git a/lib/ui/views/home/home_view.dart b/lib/ui/views/home/home_view.dart index 2d3317c6..e4b418cd 100644 --- a/lib/ui/views/home/home_view.dart +++ b/lib/ui/views/home/home_view.dart @@ -21,84 +21,103 @@ class HomeView extends StatelessWidget { onModelReady: (model) => model.initialize(), viewModelBuilder: () => locator(), builder: (context, model, child) => Scaffold( - body: SafeArea( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 60), - I18nText( - 'homeView.widgetTitle', - child: Text( - '', - style: GoogleFonts.inter( - fontSize: 28, - fontWeight: FontWeight.w500, - ), + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + snap: false, + floating: false, + expandedHeight: 100.0, + automaticallyImplyLeading: false, + backgroundColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.scrolledUnder) + ? isDark + ? Theme.of(context).colorScheme.primary + : Theme.of(context).navigationBarTheme.backgroundColor! + : Theme.of(context).scaffoldBackgroundColor, + ), + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.symmetric( + vertical: 23.0, + horizontal: 20.0, + ), + title: I18nText( + 'homeView.widgetTitle', + child: Text( + '', + style: GoogleFonts.inter( + color: Theme.of(context).textTheme.headline5!.color, + fontWeight: FontWeight.w500, ), ), - const SizedBox(height: 23), - I18nText( - 'homeView.updatesSubtitle', - child: Text( - '', - style: GoogleFonts.inter( - fontSize: 20, - fontWeight: FontWeight.w500, - color: isDark - ? const Color(0xffD1E1FA) - : const Color(0xff384E6E), - ), - ), - ), - const SizedBox(height: 10), - LatestCommitCard( - onPressed: () => model.updateManager(context), - color: Theme.of(context).colorScheme.primary, - ), - const SizedBox(height: 23), - I18nText( - 'homeView.patchedSubtitle', - child: Text( - '', - style: GoogleFonts.inter( - fontSize: 20, - color: isDark - ? const Color(0xffD1E1FA) - : const Color(0xff384E6E), - ), - ), - ), - const SizedBox(height: 8), - Row( - children: [ - DashboardChip( - label: "homeView.updatesAvailable", - isSelected: model.showUpdatableApps, - onSelected: (value) { - model.toggleUpdatableApps(true); - }, - ), - const SizedBox(width: 10), - DashboardChip( - label: "homeView.installed", - isSelected: !model.showUpdatableApps, - onSelected: (value) { - model.toggleUpdatableApps(false); - }, - ) - ], - ), - const SizedBox(height: 14), - model.showUpdatableApps - ? AvailableUpdatesCard() - : InstalledAppsCard() - ], + ), ), ), - ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + sliver: SliverList( + delegate: SliverChildListDelegate.fixed( + [ + I18nText( + 'homeView.updatesSubtitle', + child: Text( + '', + style: GoogleFonts.inter( + fontSize: 20, + fontWeight: FontWeight.w500, + color: isDark + ? const Color(0xffD1E1FA) + : const Color(0xff384E6E), + ), + ), + ), + const SizedBox(height: 10), + LatestCommitCard( + onPressed: () => model.updateManager(context), + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 23), + I18nText( + 'homeView.patchedSubtitle', + child: Text( + '', + style: GoogleFonts.inter( + fontSize: 20, + color: isDark + ? const Color(0xffD1E1FA) + : const Color(0xff384E6E), + ), + ), + ), + const SizedBox(height: 8), + Row( + children: [ + DashboardChip( + label: "homeView.updatesAvailable", + isSelected: model.showUpdatableApps, + onSelected: (value) { + model.toggleUpdatableApps(true); + }, + ), + const SizedBox(width: 10), + DashboardChip( + label: "homeView.installed", + isSelected: !model.showUpdatableApps, + onSelected: (value) { + model.toggleUpdatableApps(false); + }, + ) + ], + ), + const SizedBox(height: 14), + model.showUpdatableApps + ? AvailableUpdatesCard() + : InstalledAppsCard() + ], + ), + ), + ), + ], ), ), ); diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index 1b0a9d2c..a5606a54 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -15,146 +15,170 @@ class InstallerView extends StatelessWidget { viewModelBuilder: () => InstallerViewModel(), builder: (context, model, child) => WillPopScope( child: Scaffold( - body: SafeArea( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 20), - controller: model.scrollController, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 60), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - model.headerLogs, - style: GoogleFonts.inter( - fontSize: 28, - fontWeight: FontWeight.w500, + body: CustomScrollView( + controller: model.scrollController, + slivers: [ + SliverAppBar( + pinned: true, + snap: false, + floating: false, + expandedHeight: 100.0, + automaticallyImplyLeading: false, + backgroundColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.scrolledUnder) + ? isDark + ? Theme.of(context).colorScheme.primary + : Theme.of(context) + .navigationBarTheme + .backgroundColor! + : Theme.of(context).scaffoldBackgroundColor, + ), + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.symmetric( + vertical: 23.0, + horizontal: 20.0, + ), + title: Text( + model.headerLogs, + style: GoogleFonts.inter( + color: Theme.of(context).textTheme.headline5!.color, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + sliver: SliverList( + delegate: SliverChildListDelegate.fixed( + [ + Padding( + padding: const EdgeInsets.only( + left: 4.0, + top: 0.0, + right: 4.0, + bottom: 16.0, + ), + child: LinearProgressIndicator( + color: Theme.of(context).colorScheme.secondary, + backgroundColor: Colors.white, + value: model.progress, + ), + ), + Container( + padding: const EdgeInsets.all(12.0), + width: double.infinity, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + model.logs, + style: GoogleFonts.jetBrainsMono( + fontSize: 13, + height: 1.5, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 16, horizontal: 0), + child: Visibility( + visible: !model.isPatching, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + //TODO: Move to separate file + TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all( + const EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + side: BorderSide( + width: 1, + color: Theme.of(context) + .colorScheme + .secondary, + ), + ), + ), + side: MaterialStateProperty.all( + BorderSide( + color: Theme.of(context) + .iconTheme + .color! + .withOpacity(0.4), + width: 1, + ), + ), + backgroundColor: MaterialStateProperty.all( + isDark + ? Theme.of(context) + .colorScheme + .background + : Colors.white, + ), + foregroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.secondary, + ), + ), + onPressed: () => model.shareResult(), + child: I18nText("Share file"), + ), + const SizedBox(width: 16), + TextButton( + onPressed: () { + if (model.isInstalled) { + model.openApp(); + Navigator.of(context).pop(); + } else { + model.installResult(); + } + }, + style: ButtonStyle( + padding: MaterialStateProperty.all( + const EdgeInsets.symmetric( + horizontal: 24, + vertical: 8, + ), + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + side: BorderSide( + width: 1, + color: Theme.of(context) + .colorScheme + .secondary, + ), + ), + ), + backgroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.secondary, + ), + foregroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.background, + ), + ), + child: I18nText(model.isInstalled + ? 'installerView.openButton' + : 'installerView.installButton'), + ), + ], + ), ), ), ], ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 16.0, - horizontal: 4.0, - ), - child: LinearProgressIndicator( - color: Theme.of(context).colorScheme.secondary, - backgroundColor: Colors.white, - value: model.progress, - ), - ), - Container( - padding: const EdgeInsets.all(12.0), - width: double.infinity, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - model.logs, - style: GoogleFonts.jetBrainsMono( - fontSize: 13, - height: 1.5, - ), - ), - ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 16, horizontal: 0), - child: Visibility( - visible: !model.isPatching, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - //TODO: Move to separate file - TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all( - const EdgeInsets.symmetric( - horizontal: 20, - vertical: 12, - ), - ), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(100), - side: BorderSide( - width: 1, - color: - Theme.of(context).colorScheme.secondary, - ), - ), - ), - side: MaterialStateProperty.all( - BorderSide( - color: Theme.of(context) - .iconTheme - .color! - .withOpacity(0.4), - width: 1, - ), - ), - backgroundColor: MaterialStateProperty.all( - isDark - ? Theme.of(context).colorScheme.background - : Colors.white, - ), - foregroundColor: MaterialStateProperty.all( - Theme.of(context).colorScheme.secondary, - ), - ), - onPressed: () => model.shareResult(), - child: I18nText("Share file"), - ), - const SizedBox(width: 16), - TextButton( - onPressed: () { - if (model.isInstalled) { - model.openApp(); - Navigator.of(context).pop(); - } else { - model.installResult(); - } - }, - style: ButtonStyle( - padding: MaterialStateProperty.all( - const EdgeInsets.symmetric( - horizontal: 24, - vertical: 8, - ), - ), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(100), - side: BorderSide( - width: 1, - color: - Theme.of(context).colorScheme.secondary, - ), - ), - ), - backgroundColor: MaterialStateProperty.all( - Theme.of(context).colorScheme.secondary, - ), - foregroundColor: MaterialStateProperty.all( - Theme.of(context).colorScheme.background, - ), - ), - child: I18nText(model.isInstalled - ? 'installerView.fabOpenButton' - : 'installerView.fabInstallButton'), - ), - ], - ), - ), - ), - ], + ), ), - ), + ], ), ), onWillPop: () async { diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index 6d4b0e22..834d9dfc 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -122,7 +122,7 @@ class InstallerViewModel extends BaseViewModel { resourcePatching = true; } await _patcherAPI.mergeIntegrations(mergeIntegrations); - headerLogs = "Merging integrations"; + headerLogs = 'Merging integrations'; await _patcherAPI.runPatcher( apkFilePath, _patches, diff --git a/lib/ui/views/patcher/patcher_view.dart b/lib/ui/views/patcher/patcher_view.dart index bf53dd5c..deb04bf2 100644 --- a/lib/ui/views/patcher/patcher_view.dart +++ b/lib/ui/views/patcher/patcher_view.dart @@ -27,43 +27,64 @@ class PatcherView extends StatelessWidget { foregroundColor: Colors.white, ), ), - body: SafeArea( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 60), - I18nText( + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + snap: false, + floating: false, + expandedHeight: 100.0, + automaticallyImplyLeading: false, + backgroundColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.scrolledUnder) + ? isDark + ? Theme.of(context).colorScheme.primary + : Theme.of(context).navigationBarTheme.backgroundColor! + : Theme.of(context).scaffoldBackgroundColor, + ), + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.symmetric( + vertical: 23.0, + horizontal: 20.0, + ), + title: I18nText( 'patcherView.widgetTitle', child: Text( '', style: GoogleFonts.inter( - fontSize: 28, + color: Theme.of(context).textTheme.headline5!.color, fontWeight: FontWeight.w500, ), ), ), - const SizedBox(height: 23), - AppSelectorCard( - onPressed: model.navigateToAppSelector, - color: Theme.of(context).colorScheme.primary, - ), - const SizedBox(height: 16), - Opacity( - opacity: isDark - ? (model.dimPatchesCard() ? 0.5 : 1) - : (model.dimPatchesCard() ? 0.75 : 1), - child: PatchSelectorCard( - onPressed: model.dimPatchesCard() - ? () => {} - : model.navigateToPatchesSelector, - color: Theme.of(context).colorScheme.primary, - ), - ), - ], + ), ), - ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + sliver: SliverList( + delegate: SliverChildListDelegate.fixed( + [ + AppSelectorCard( + onPressed: model.navigateToAppSelector, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 16), + Opacity( + opacity: isDark + ? (model.dimPatchesCard() ? 0.5 : 1) + : (model.dimPatchesCard() ? 0.75 : 1), + child: PatchSelectorCard( + onPressed: model.dimPatchesCard() + ? () => {} + : model.navigateToPatchesSelector, + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + ), + ), + ), + ], ), ), ); diff --git a/lib/ui/views/settings/settings_view.dart b/lib/ui/views/settings/settings_view.dart index 695537ec..122934d0 100644 --- a/lib/ui/views/settings/settings_view.dart +++ b/lib/ui/views/settings/settings_view.dart @@ -26,134 +26,150 @@ class SettingsView extends StatelessWidget { viewModelBuilder: () => SettingsViewModel(), onModelReady: (model) => model.initialize(), builder: (context, SettingsViewModel model, child) => Scaffold( - body: SingleChildScrollView( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 60), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: I18nText( - 'settingsView.widgetTitle', - child: Text( - '', - style: GoogleFonts.inter( - fontSize: 28, - fontWeight: FontWeight.w500, - ), - ), + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + snap: false, + floating: false, + expandedHeight: 100.0, + automaticallyImplyLeading: false, + backgroundColor: MaterialStateColor.resolveWith( + (states) => states.contains(MaterialState.scrolledUnder) + ? isDark + ? Theme.of(context).colorScheme.primary + : Theme.of(context).navigationBarTheme.backgroundColor! + : Theme.of(context).scaffoldBackgroundColor, + ), + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.symmetric( + vertical: 23.0, + horizontal: 20.0, + ), + title: I18nText( + 'homeView.widgetTitle', + child: Text( + '', + style: GoogleFonts.inter( + color: Theme.of(context).textTheme.headline5!.color, + fontWeight: FontWeight.w500, ), ), - const SizedBox(height: 12), - SettingsSwitchItem( - title: 'settingsView.themeLabel', - subtitle: 'settingsView.themeHint', - value: isDark, - onTap: (value) { - isDark = value; - getThemeManager(context).toggleDarkLightTheme(); - }, - ), - ListTile( - title: I18nText( - 'settingsView.rootModeLabel', - child: Text( - '', - style: kSettingItemTextStyle, - ), - ), - subtitle: I18nText('settingsView.rootModeHint'), - trailing: GestureDetector( - onTap: () { - model.navigateToRootChecker(); - }, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 16, vertical: 8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - width: 1, - color: Theme.of(context).colorScheme.secondary, - ), - ), - child: Text( - model.isRooted ? 'Rooted' : 'Not rooted', - ), - ), - ), - ), - CustomTextField( - inputController: organizationController, - label: 'settingsView.organizationLabel', - hint: ghOrg, - onChanged: (value) { - ghOrg = value; - }, - ), - CustomTextField( - inputController: patchesSourceController, - label: 'settingsView.patchesSourceLabel', - hint: patchesRepo, - onChanged: (value) { - patchesRepo = value; - }, - ), - CustomTextField( - inputController: integrationsSourceController, - label: 'settingsView.integrationsSourceLabel', - hint: integrationsRepo, - onChanged: (value) { - integrationsRepo = value; - }, - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - I18nText( - 'settingsView.languageLabel', - child: Text('', style: kSettingItemTextStyle), - ), - DropdownButton( - value: 'en', - items: const [ - DropdownMenuItem( - value: 'en', - child: Text('English'), - ), - DropdownMenuItem( - value: 'fr', - child: Text('French'), - ), - ], - onChanged: (value) { - value = value; - }, - ), - ], - ), - ), - ListTile( - title: I18nText( - 'settingsView.contributorsLabel', - child: Text('', style: kSettingItemTextStyle), - ), - onTap: model.navigateToContributors, - ), - const SocialMediaCards(), - const AboutWidget(), - ], + ), ), ), - ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + sliver: SliverList( + delegate: SliverChildListDelegate.fixed( + [ + SettingsSwitchItem( + title: 'settingsView.themeLabel', + subtitle: 'settingsView.themeHint', + value: isDark, + onTap: (value) { + isDark = value; + getThemeManager(context).toggleDarkLightTheme(); + }, + ), + ListTile( + title: I18nText( + 'settingsView.rootModeLabel', + child: Text( + '', + style: kSettingItemTextStyle, + ), + ), + subtitle: I18nText('settingsView.rootModeHint'), + trailing: GestureDetector( + onTap: () { + model.navigateToRootChecker(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + width: 1, + color: Theme.of(context).colorScheme.secondary, + ), + ), + child: Text( + model.isRooted ? 'Rooted' : 'Not rooted', + ), + ), + ), + ), + CustomTextField( + inputController: organizationController, + label: 'settingsView.organizationLabel', + hint: ghOrg, + onChanged: (value) { + ghOrg = value; + }, + ), + CustomTextField( + inputController: patchesSourceController, + label: 'settingsView.patchesSourceLabel', + hint: patchesRepo, + onChanged: (value) { + patchesRepo = value; + }, + ), + CustomTextField( + inputController: integrationsSourceController, + label: 'settingsView.integrationsSourceLabel', + hint: integrationsRepo, + onChanged: (value) { + integrationsRepo = value; + }, + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + I18nText( + 'settingsView.languageLabel', + child: Text('', style: kSettingItemTextStyle), + ), + DropdownButton( + value: 'en', + items: const [ + DropdownMenuItem( + value: 'en', + child: Text('English'), + ), + DropdownMenuItem( + value: 'fr', + child: Text('French'), + ), + ], + onChanged: (value) { + value = value; + }, + ), + ], + ), + ), + ListTile( + title: I18nText( + 'settingsView.contributorsLabel', + child: Text('', style: kSettingItemTextStyle), + ), + onTap: model.navigateToContributors, + ), + const SocialMediaCards(), + const AboutWidget(), + ], + ), + ), + ), + ], ), ), );