ente/lib/ui/common/progress_dialog.dart

288 lines
8.1 KiB
Dart

import 'package:flutter/material.dart';
enum ProgressDialogType { normal, download }
String _dialogMessage = "Loading...";
double _progress = 0.0, _maxProgress = 100.0;
Widget? _customBody;
TextAlign _textAlign = TextAlign.left;
Alignment _progressWidgetAlignment = Alignment.centerLeft;
TextDirection _direction = TextDirection.ltr;
bool _isShowing = false;
BuildContext? _context, _dismissingContext;
ProgressDialogType? _progressDialogType;
bool _barrierDismissible = true, _showLogs = false;
Color? _barrierColor;
TextStyle _progressTextStyle = const TextStyle(
color: Colors.black,
fontSize: 12.0,
fontWeight: FontWeight.w400,
),
_messageStyle = const TextStyle(
color: Colors.black,
fontSize: 18.0,
fontWeight: FontWeight.w600,
);
double _dialogElevation = 8.0, _borderRadius = 8.0;
Color _backgroundColor = Colors.white;
Curve _insetAnimCurve = Curves.easeInOut;
EdgeInsets _dialogPadding = const EdgeInsets.all(8.0);
Widget _progressWidget = Image.asset(
'assets/double_ring_loading_io.gif',
package: 'progress_dialog',
);
class ProgressDialog {
_Body? _dialog;
ProgressDialog(
BuildContext context, {
ProgressDialogType? type,
bool? isDismissible,
bool? showLogs,
TextDirection? textDirection,
Widget? customBody,
Color? barrierColor,
}) {
_context = context;
_progressDialogType = type ?? ProgressDialogType.normal;
_barrierDismissible = isDismissible ?? true;
_showLogs = showLogs ?? false;
_customBody = customBody;
_direction = textDirection ?? TextDirection.ltr;
_barrierColor = barrierColor ?? barrierColor;
}
void style({
Widget? child,
double? progress,
double? maxProgress,
String? message,
Widget? progressWidget,
Color? backgroundColor,
TextStyle? progressTextStyle,
TextStyle? messageTextStyle,
double? elevation,
TextAlign? textAlign,
double? borderRadius,
Curve? insetAnimCurve,
EdgeInsets? padding,
Alignment? progressWidgetAlignment,
}) {
if (_isShowing) return;
if (_progressDialogType == ProgressDialogType.download) {
_progress = progress ?? _progress;
}
_dialogMessage = message ?? _dialogMessage;
_maxProgress = maxProgress ?? _maxProgress;
_progressWidget = progressWidget ?? _progressWidget;
_backgroundColor = backgroundColor ?? _backgroundColor;
_messageStyle = messageTextStyle ?? _messageStyle;
_progressTextStyle = progressTextStyle ?? _progressTextStyle;
_dialogElevation = elevation ?? _dialogElevation;
_borderRadius = borderRadius ?? _borderRadius;
_insetAnimCurve = insetAnimCurve ?? _insetAnimCurve;
_textAlign = textAlign ?? _textAlign;
_progressWidget = child ?? _progressWidget;
_dialogPadding = padding ?? _dialogPadding;
_progressWidgetAlignment =
progressWidgetAlignment ?? _progressWidgetAlignment;
}
void update({
double? progress,
double? maxProgress,
String? message,
Widget? progressWidget,
TextStyle? progressTextStyle,
TextStyle? messageTextStyle,
}) {
if (_progressDialogType == ProgressDialogType.download) {
_progress = progress ?? _progress;
}
_dialogMessage = message ?? _dialogMessage;
_maxProgress = maxProgress ?? _maxProgress;
_progressWidget = progressWidget ?? _progressWidget;
_messageStyle = messageTextStyle ?? _messageStyle;
_progressTextStyle = progressTextStyle ?? _progressTextStyle;
if (_isShowing) _dialog!.update();
}
bool isShowing() {
return _isShowing;
}
Future<bool> hide() async {
try {
if (_isShowing) {
_isShowing = false;
if (_dismissingContext != null) {
Navigator.of(_dismissingContext!).pop();
}
if (_showLogs) debugPrint('ProgressDialog dismissed');
return Future.value(true);
} else {
if (_showLogs) debugPrint('ProgressDialog already dismissed');
return Future.value(false);
}
} catch (err) {
debugPrint('Seems there is an issue hiding dialog');
debugPrint(err.toString());
return Future.value(false);
}
}
Future<bool> show() async {
try {
if (!_isShowing) {
_dialog = _Body();
showDialog<dynamic>(
context: _context!,
barrierDismissible: _barrierDismissible,
barrierColor: _barrierColor,
builder: (BuildContext context) {
_dismissingContext = context;
return WillPopScope(
onWillPop: () async => _barrierDismissible,
child: Dialog(
backgroundColor: _backgroundColor,
insetAnimationCurve: _insetAnimCurve,
insetAnimationDuration: const Duration(milliseconds: 100),
elevation: _dialogElevation,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(_borderRadius)),
),
child: _dialog,
),
);
},
);
// Delaying the function for 200 milliseconds
// [Default transitionDuration of DialogRoute]
await Future.delayed(const Duration(milliseconds: 200));
if (_showLogs) debugPrint('ProgressDialog shown');
_isShowing = true;
return true;
} else {
if (_showLogs) debugPrint("ProgressDialog already shown/showing");
return false;
}
} catch (err) {
_isShowing = false;
debugPrint('Exception while showing the dialog');
debugPrint(err.toString());
return false;
}
}
}
// ignore: must_be_immutable
class _Body extends StatefulWidget {
final _BodyState _dialog = _BodyState();
update() {
_dialog.update();
}
@override
State<StatefulWidget> createState() {
return _dialog;
}
}
class _BodyState extends State<_Body> {
update() {
setState(() {});
}
@override
void dispose() {
_isShowing = false;
if (_showLogs) debugPrint('ProgressDialog dismissed by back button');
super.dispose();
}
@override
Widget build(BuildContext context) {
final loader = Align(
alignment: _progressWidgetAlignment,
child: SizedBox(
width: 60.0,
height: 60.0,
child: _progressWidget,
),
);
final text = Expanded(
child: _progressDialogType == ProgressDialogType.normal
? Text(
_dialogMessage,
textAlign: _textAlign,
style: _messageStyle,
textDirection: _direction,
)
: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(height: 8.0),
Row(
children: <Widget>[
Expanded(
child: Text(
_dialogMessage,
style: _messageStyle,
textDirection: _direction,
),
),
],
),
const SizedBox(height: 4.0),
Align(
alignment: Alignment.bottomRight,
child: Text(
"$_progress/$_maxProgress",
style: _progressTextStyle,
textDirection: _direction,
),
),
],
),
),
);
return _customBody ??
Container(
padding: _dialogPadding,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
// row body
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(width: 8.0),
_direction == TextDirection.ltr ? loader : text,
const SizedBox(width: 8.0),
_direction == TextDirection.rtl ? loader : text,
const SizedBox(width: 8.0)
],
),
],
),
);
}
}