feat: add Sliver App Bar to Home, Patcher, Installer and Settings views

This commit is contained in:
Alberto Ponces 2022-08-24 13:22:36 +01:00
parent e6b540d32b
commit 5041a30fb5
6 changed files with 444 additions and 364 deletions

View File

@ -59,8 +59,8 @@
}, },
"installerView": { "installerView": {
"widgetTitle": "Installer", "widgetTitle": "Installer",
"fabInstallButton": "Install", "installButton": "Install",
"fabOpenButton": "Open", "openButton": "Open",
"notificationTitle": "ReVanced Manager is patching", "notificationTitle": "ReVanced Manager is patching",
"notificationText": "Tap to return to the installer" "notificationText": "Tap to return to the installer"
}, },

View File

@ -21,84 +21,103 @@ class HomeView extends StatelessWidget {
onModelReady: (model) => model.initialize(), onModelReady: (model) => model.initialize(),
viewModelBuilder: () => locator<HomeViewModel>(), viewModelBuilder: () => locator<HomeViewModel>(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
body: SafeArea( body: CustomScrollView(
child: SingleChildScrollView( slivers: <Widget>[
child: Padding( SliverAppBar(
padding: const EdgeInsets.symmetric(horizontal: 20.0), pinned: true,
child: Column( snap: false,
crossAxisAlignment: CrossAxisAlignment.start, floating: false,
children: [ expandedHeight: 100.0,
const SizedBox(height: 60), automaticallyImplyLeading: false,
I18nText( backgroundColor: MaterialStateColor.resolveWith(
'homeView.widgetTitle', (states) => states.contains(MaterialState.scrolledUnder)
child: Text( ? isDark
'', ? Theme.of(context).colorScheme.primary
style: GoogleFonts.inter( : Theme.of(context).navigationBarTheme.backgroundColor!
fontSize: 28, : Theme.of(context).scaffoldBackgroundColor,
fontWeight: FontWeight.w500, ),
), 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(
<Widget>[
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()
],
),
),
),
],
), ),
), ),
); );

View File

@ -15,146 +15,170 @@ class InstallerView extends StatelessWidget {
viewModelBuilder: () => InstallerViewModel(), viewModelBuilder: () => InstallerViewModel(),
builder: (context, model, child) => WillPopScope( builder: (context, model, child) => WillPopScope(
child: Scaffold( child: Scaffold(
body: SafeArea( body: CustomScrollView(
child: SingleChildScrollView( controller: model.scrollController,
padding: const EdgeInsets.symmetric(horizontal: 20), slivers: <Widget>[
controller: model.scrollController, SliverAppBar(
child: Column( pinned: true,
crossAxisAlignment: CrossAxisAlignment.start, snap: false,
children: <Widget>[ floating: false,
const SizedBox(height: 60), expandedHeight: 100.0,
Row( automaticallyImplyLeading: false,
crossAxisAlignment: CrossAxisAlignment.start, backgroundColor: MaterialStateColor.resolveWith(
mainAxisAlignment: MainAxisAlignment.spaceBetween, (states) => states.contains(MaterialState.scrolledUnder)
children: [ ? isDark
Text( ? Theme.of(context).colorScheme.primary
model.headerLogs, : Theme.of(context)
style: GoogleFonts.inter( .navigationBarTheme
fontSize: 28, .backgroundColor!
fontWeight: FontWeight.w500, : 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(
<Widget>[
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 { onWillPop: () async {

View File

@ -122,7 +122,7 @@ class InstallerViewModel extends BaseViewModel {
resourcePatching = true; resourcePatching = true;
} }
await _patcherAPI.mergeIntegrations(mergeIntegrations); await _patcherAPI.mergeIntegrations(mergeIntegrations);
headerLogs = "Merging integrations"; headerLogs = 'Merging integrations';
await _patcherAPI.runPatcher( await _patcherAPI.runPatcher(
apkFilePath, apkFilePath,
_patches, _patches,

View File

@ -27,43 +27,64 @@ class PatcherView extends StatelessWidget {
foregroundColor: Colors.white, foregroundColor: Colors.white,
), ),
), ),
body: SafeArea( body: CustomScrollView(
child: SingleChildScrollView( slivers: <Widget>[
padding: const EdgeInsets.symmetric(horizontal: 20.0), SliverAppBar(
child: Column( pinned: true,
crossAxisAlignment: CrossAxisAlignment.start, snap: false,
children: [ floating: false,
const SizedBox(height: 60), expandedHeight: 100.0,
I18nText( 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', 'patcherView.widgetTitle',
child: Text( child: Text(
'', '',
style: GoogleFonts.inter( style: GoogleFonts.inter(
fontSize: 28, color: Theme.of(context).textTheme.headline5!.color,
fontWeight: FontWeight.w500, 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(
<Widget>[
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,
),
),
],
),
),
),
],
), ),
), ),
); );

View File

@ -26,134 +26,150 @@ class SettingsView extends StatelessWidget {
viewModelBuilder: () => SettingsViewModel(), viewModelBuilder: () => SettingsViewModel(),
onModelReady: (model) => model.initialize(), onModelReady: (model) => model.initialize(),
builder: (context, SettingsViewModel model, child) => Scaffold( builder: (context, SettingsViewModel model, child) => Scaffold(
body: SingleChildScrollView( body: CustomScrollView(
child: SafeArea( slivers: <Widget>[
child: Padding( SliverAppBar(
padding: const EdgeInsets.symmetric(horizontal: 12.0), pinned: true,
child: Column( snap: false,
crossAxisAlignment: CrossAxisAlignment.start, floating: false,
children: <Widget>[ expandedHeight: 100.0,
const SizedBox(height: 60), automaticallyImplyLeading: false,
Padding( backgroundColor: MaterialStateColor.resolveWith(
padding: const EdgeInsets.symmetric(horizontal: 8.0), (states) => states.contains(MaterialState.scrolledUnder)
child: I18nText( ? isDark
'settingsView.widgetTitle', ? Theme.of(context).colorScheme.primary
child: Text( : Theme.of(context).navigationBarTheme.backgroundColor!
'', : Theme.of(context).scaffoldBackgroundColor,
style: GoogleFonts.inter( ),
fontSize: 28, flexibleSpace: FlexibleSpaceBar(
fontWeight: FontWeight.w500, 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(
<Widget>[
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(),
],
),
),
),
],
), ),
), ),
); );