Add a cloud-shaped progress indicator

This commit is contained in:
Ming Ming 2022-11-24 21:50:08 +08:00
parent 76e804dc8b
commit 173817dadf
3 changed files with 154 additions and 8 deletions

View file

@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
/// A progress indicator that looks like a cloud
class CloudProgressIndicator extends StatefulWidget {
const CloudProgressIndicator({
super.key,
required this.size,
this.value,
});
@override
State<StatefulWidget> createState() => _CloudProgressIndicatorState();
final double size;
final double? value;
}
class _CloudProgressIndicatorState extends State<CloudProgressIndicator>
with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
if (widget.value == null) {
_startIntermediateAnimation();
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
void didUpdateWidget(CloudProgressIndicator oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.value == null && !_controller.isAnimating) {
_startIntermediateAnimation();
} else if (widget.value != null && _controller.isAnimating) {
_controller.stop();
_isInvert = false;
}
}
@override
Widget build(BuildContext context) {
return _Indicator(
size: widget.size,
value: widget.value,
controller: _controller,
isInvert: _isInvert,
);
}
void _startIntermediateAnimation() {
_controller
..forward(from: 0)
..addStatusListener((status) {
if (mounted) {
if (status == AnimationStatus.completed) {
setState(() {
_isInvert = true;
});
_controller.reverse(from: 1);
} else if (status == AnimationStatus.dismissed) {
setState(() {
_isInvert = false;
});
_controller.forward(from: 0);
}
}
});
}
late final _controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 3500),
);
var _isInvert = false;
}
class _Indicator extends AnimatedWidget {
const _Indicator({
required this.size,
required this.value,
required this.isInvert,
required AnimationController controller,
}) : super(listenable: controller);
@override
Widget build(BuildContext context) {
final thisValue = value ?? _progress.value;
final stroke = size / 12;
const curve = Curves.easeInOutQuad;
return Transform.scale(
scaleX: isInvert ? -1 : 1,
child: Container(
width: size,
padding: EdgeInsets.all(stroke / 2),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 1,
child: AspectRatio(
aspectRatio: 1,
child: CircularProgressIndicator(
strokeWidth: stroke,
value: curve.transform((thisValue * 3).clamp(0, 1)),
),
),
),
SizedBox(width: stroke / 2),
Expanded(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: CircularProgressIndicator(
strokeWidth: stroke,
value: curve.transform((thisValue * 3 - 1).clamp(0, 1)),
),
),
),
SizedBox(width: stroke / 2),
Expanded(
flex: 1,
child: AspectRatio(
aspectRatio: 1,
child: CircularProgressIndicator(
strokeWidth: stroke,
value: curve.transform((thisValue * 3 - 2).clamp(0, 1)),
),
),
),
],
),
),
);
}
Animation<double> get _progress => listenable as Animation<double>;
final double size;
final double? value;
final bool isInvert;
}

View file

@ -20,6 +20,7 @@ import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/string_extension.dart';
import 'package:nc_photos/url_launcher_util.dart';
import 'package:nc_photos/use_case/ls_single_file.dart';
import 'package:nc_photos/widget/cloud_progress_indicator.dart';
class ConnectArguments {
ConnectArguments(this.uri);
@ -88,15 +89,12 @@ class _ConnectState extends State<Connect> {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.cloud,
size: 128,
color: Theme.of(context).colorScheme.primary,
),
const CloudProgressIndicator(size: 192),
const SizedBox(height: 16),
Text(
L10n.global().connectingToServer(widget.uri),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline6,
style: Theme.of(context).textTheme.titleLarge,
)
],
),

View file

@ -9,8 +9,8 @@ import 'package:nc_photos/pref.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/url_launcher_util.dart';
import 'package:nc_photos/widget/account_picker_dialog.dart';
import 'package:nc_photos/widget/app_bar_circular_progress_indicator.dart';
import 'package:nc_photos/widget/app_bar_title_container.dart';
import 'package:nc_photos/widget/cloud_progress_indicator.dart';
import 'package:nc_photos/widget/settings.dart';
import 'package:nc_photos/widget/translucent_sliver_app_bar.dart';
@ -42,7 +42,7 @@ class HomeSliverAppBar extends StatelessWidget {
title: Text(accountLabel ?? account.address),
subtitle: accountLabel == null ? Text(account.username2) : null,
icon: isShowProgressIcon
? const AppBarCircularProgressIndicator()
? const CloudProgressIndicator(size: 36)
: (account.scheme == "http"
? Icon(
Icons.no_encryption_outlined,