Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui): Support free-scroll and auto-scroll for the installer logs (#1736) #1836

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
87 changes: 54 additions & 33 deletions lib/ui/views/installer/installer_view.dart
Expand Up @@ -75,43 +75,64 @@ class InstallerView extends StatelessWidget {
),
),
),
body: CustomScrollView(
controller: model.scrollController,
slivers: <Widget>[
CustomSliverAppBar(
title: Text(
model.headerLogs,
style: GoogleFonts.inter(
color: Theme.of(context).textTheme.titleLarge!.color,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
onBackButtonPressed: () => Navigator.maybePop(context),
bottom: PreferredSize(
preferredSize: const Size(double.infinity, 1.0),
child: GradientProgressIndicator(progress: model.progress),
),
),
SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: SliverList(
delegate: SliverChildListDelegate.fixed(
<Widget>[
CustomCard(
child: Text(
model.logs,
style: GoogleFonts.jetBrainsMono(
fontSize: 13,
height: 1.5,
),
body: NotificationListener<ScrollNotification>(
onNotification: model.handleAutoScrollNotification,
child: Stack(
children: [
CustomScrollView(
key: model.logCustomScrollKey,
controller: model.scrollController,
slivers: <Widget>[
CustomSliverAppBar(
title: Text(
model.headerLogs,
style: GoogleFonts.inter(
color:
Theme.of(context).textTheme.titleLarge!.color,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
onBackButtonPressed: () => Navigator.maybePop(context),
bottom: PreferredSize(
preferredSize: const Size(double.infinity, 1.0),
child: GradientProgressIndicator(
progress: model.progress,
),
),
),
SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: SliverList(
delegate: SliverChildListDelegate.fixed(
<Widget>[
CustomCard(
child: Text(
model.logs,
style: GoogleFonts.jetBrainsMono(
fontSize: 13,
height: 1.5,
),
),
),
],
),
),
],
),
],
),
Visibility(
visible: model.showAutoScrollButton,
child: Align(
alignment: const Alignment(0.9, 0.97),
child: FloatingActionButton(
onPressed: model.scrollToBottom,
child: const Icon(Icons.arrow_downward_rounded),
),
),
),
),
],
],
),
),
),
),
Expand Down
62 changes: 55 additions & 7 deletions lib/ui/views/installer/installer_viewmodel.dart
Expand Up @@ -30,6 +30,7 @@ class InstallerViewModel extends BaseViewModel {
static const _installerChannel = MethodChannel(
'app.revanced.manager.flutter/installer',
);
final Key logCustomScrollKey = UniqueKey();
final ScrollController scrollController = ScrollController();
final ScreenshotCallback screenshotCallback = ScreenshotCallback();
double? progress = 0.0;
Expand All @@ -44,6 +45,57 @@ class InstallerViewModel extends BaseViewModel {
bool cancel = false;
bool showPopupScreenshotWarning = true;

bool showAutoScrollButton = false;
bool _isAutoScrollEnabled = true;
bool _isAutoScrolling = false;

double get getCurrentScrollPercentage {
final maxScrollExtent = scrollController.position.maxScrollExtent;
final currentPosition = scrollController.position.pixels;

return currentPosition / maxScrollExtent;
}

bool handleAutoScrollNotification(ScrollNotification event) {
if (_isAutoScrollEnabled && event is ScrollStartNotification) {
_isAutoScrollEnabled = _isAutoScrolling;
showAutoScrollButton = false;
notifyListeners();

return true;
}

if (event is ScrollEndNotification) {
const anchorThreshold = 0.987;

_isAutoScrollEnabled =
_isAutoScrolling || getCurrentScrollPercentage >= anchorThreshold;

showAutoScrollButton = !_isAutoScrollEnabled && !_isAutoScrolling;
notifyListeners();

return true;
}

return false;
}

void scrollToBottom() {
_isAutoScrolling = true;

WidgetsBinding.instance.addPostFrameCallback((_) async {
final maxScrollExtent = scrollController.position.maxScrollExtent;

await scrollController.animateTo(
maxScrollExtent,
duration: const Duration(milliseconds: 100),
curve: Curves.fastOutSlowIn,
);

_isAutoScrolling = false;
});
}

Future<void> initialize(BuildContext context) async {
isRooted = await _rootAPI.isRooted();
if (await Permission.ignoreBatteryOptimizations.isGranted) {
Expand Down Expand Up @@ -123,13 +175,9 @@ class InstallerViewModel extends BaseViewModel {
if (logs[logs.length - 1] == '\n') {
logs = logs.substring(0, logs.length - 1);
}
Future.delayed(const Duration(milliseconds: 100)).then((value) {
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 100),
curve: Curves.fastOutSlowIn,
);
});
if (_isAutoScrollEnabled) {
scrollToBottom();
}
}
notifyListeners();
}
Expand Down