mirror of
https://github.com/localsend/localsend.git
synced 2026-04-29 03:00:23 -04:00
feat: add more animations
This commit is contained in:
@@ -107,6 +107,10 @@
|
||||
"title": "Changelog"
|
||||
},
|
||||
"dialogs": {
|
||||
"noFiles": {
|
||||
"title": "No file selected",
|
||||
"content": "Please select at least one file."
|
||||
},
|
||||
"cancelSession": {
|
||||
"title": "Cancel file transfer",
|
||||
"content": "Do you really want to cancel the file transfer?"
|
||||
|
||||
@@ -107,6 +107,10 @@
|
||||
"title": "Changelog"
|
||||
},
|
||||
"dialogs": {
|
||||
"noFiles": {
|
||||
"title": "Keine Datei ausgewählt",
|
||||
"content": "Bitte wähle mindestens eine Datei aus."
|
||||
},
|
||||
"cancelSession": {
|
||||
"title": "Dateiübertragung abbrechen",
|
||||
"content": "Möchtest du wirklich die Dateiübertragung abbrechen?"
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:localsend_app/model/session_status.dart';
|
||||
import 'package:localsend_app/provider/device_info_provider.dart';
|
||||
import 'package:localsend_app/provider/network/send_provider.dart';
|
||||
import 'package:localsend_app/util/sleep.dart';
|
||||
import 'package:localsend_app/widget/animations/initial_fade_transition.dart';
|
||||
import 'package:localsend_app/widget/list_tile/device_list_tile.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
|
||||
@@ -81,7 +82,11 @@ class _SendPageState extends ConsumerState<SendPage> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Icon(Icons.arrow_downward),
|
||||
const InitialFadeTransition(
|
||||
duration: Duration(milliseconds: 300),
|
||||
delay: Duration(milliseconds: 400),
|
||||
child: Icon(Icons.arrow_downward),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Hero(
|
||||
tag: 'device-${(sendState?.target ?? _targetDevice)?.ip}',
|
||||
@@ -93,28 +98,34 @@ class _SendPageState extends ConsumerState<SendPage> {
|
||||
),
|
||||
),
|
||||
if (sendState != null)
|
||||
...[
|
||||
if (sendState.status == SessionStatus.waiting)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Text(t.sendPage.waiting, textAlign: TextAlign.center),
|
||||
)
|
||||
else if (sendState.status == SessionStatus.declined)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Text(t.sendPage.rejected, style: const TextStyle(color: Colors.orange), textAlign: TextAlign.center),
|
||||
),
|
||||
Center(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
_cancel();
|
||||
context.pop();
|
||||
},
|
||||
icon: Icon(sendState.status == SessionStatus.declined ? Icons.check_circle : Icons.close),
|
||||
label: Text(sendState.status == SessionStatus.declined ? t.general.close : t.general.cancel),
|
||||
),
|
||||
InitialFadeTransition(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
delay: const Duration(milliseconds: 400),
|
||||
child: Column(
|
||||
children: [
|
||||
if (sendState.status == SessionStatus.waiting)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Text(t.sendPage.waiting, textAlign: TextAlign.center),
|
||||
)
|
||||
else if (sendState.status == SessionStatus.declined)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Text(t.sendPage.rejected, style: const TextStyle(color: Colors.orange), textAlign: TextAlign.center),
|
||||
),
|
||||
Center(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
_cancel();
|
||||
context.pop();
|
||||
},
|
||||
icon: Icon(sendState.status == SessionStatus.declined ? Icons.check_circle : Icons.close),
|
||||
label: Text(sendState.status == SessionStatus.declined ? t.general.close : t.general.cancel),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:localsend_app/provider/network_info_provider.dart';
|
||||
import 'package:localsend_app/provider/network/server_provider.dart';
|
||||
import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/util/ip_helper.dart';
|
||||
import 'package:localsend_app/widget/animations/initial_fade_transition.dart';
|
||||
import 'package:localsend_app/widget/rotating_widget.dart';
|
||||
|
||||
class ReceiveTab extends ConsumerStatefulWidget {
|
||||
@@ -15,11 +16,16 @@ class ReceiveTab extends ConsumerStatefulWidget {
|
||||
ConsumerState<ReceiveTab> createState() => _ReceiveTagState();
|
||||
}
|
||||
|
||||
class _ReceiveTagState extends ConsumerState<ReceiveTab> {
|
||||
class _ReceiveTagState extends ConsumerState<ReceiveTab> with AutomaticKeepAliveClientMixin {
|
||||
bool _advanced = false;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
|
||||
final settings = ref.watch(settingsProvider);
|
||||
final networkInfo = ref.watch(networkInfoProvider);
|
||||
final serverState = ref.watch(serverProvider);
|
||||
@@ -35,15 +41,26 @@ class _ReceiveTagState extends ConsumerState<ReceiveTab> {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
RotatingWidget(
|
||||
duration: const Duration(seconds: 15),
|
||||
spinning: serverState != null,
|
||||
child: Assets.img.logo512.image(width: 200),
|
||||
InitialFadeTransition(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
delay: const Duration(milliseconds: 200),
|
||||
child: RotatingWidget(
|
||||
duration: const Duration(seconds: 15),
|
||||
spinning: serverState != null,
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: Assets.img.logo512.image(height: 200),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(serverState?.alias ?? settings.alias, style: const TextStyle(fontSize: 48)),
|
||||
Text(
|
||||
serverState == null ? t.general.offline : '#${networkInfo?.localIp?.visualId ?? '?'}',
|
||||
style: const TextStyle(fontSize: 24),
|
||||
InitialFadeTransition(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
delay: const Duration(milliseconds: 500),
|
||||
child: Text(
|
||||
serverState == null ? t.general.offline : '#${networkInfo?.localIp?.visualId ?? '?'}',
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/util/file_size_helper.dart';
|
||||
import 'package:localsend_app/widget/big_button.dart';
|
||||
import 'package:localsend_app/widget/dialogs/add_file_dialog.dart';
|
||||
import 'package:localsend_app/widget/dialogs/no_files_dialog.dart';
|
||||
import 'package:localsend_app/widget/list_tile/device_list_tile.dart';
|
||||
import 'package:localsend_app/widget/rotating_widget.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
@@ -211,9 +212,15 @@ class _SendTabState extends ConsumerState<SendTab> {
|
||||
child: DeviceListTile(
|
||||
device: device,
|
||||
onTap: () {
|
||||
final files = ref.read(selectedFilesProvider);
|
||||
if (files.isEmpty) {
|
||||
context.pushBottomSheet(() => const NoFilesDialog());
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(sendProvider.notifier).startSession(
|
||||
target: device,
|
||||
files: ref.read(selectedFilesProvider),
|
||||
files: files,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
46
lib/widget/animations/initial_fade_transition.dart
Normal file
46
lib/widget/animations/initial_fade_transition.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:localsend_app/util/sleep.dart';
|
||||
|
||||
class InitialFadeTransition extends StatefulWidget {
|
||||
final Widget child;
|
||||
final Duration duration;
|
||||
final Duration delay;
|
||||
|
||||
const InitialFadeTransition({
|
||||
required this.child,
|
||||
required this.duration,
|
||||
this.delay = Duration.zero,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<InitialFadeTransition> createState() => _InitialFadeTransitionState();
|
||||
}
|
||||
|
||||
class _InitialFadeTransitionState extends State<InitialFadeTransition> {
|
||||
|
||||
double _opacity = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await sleepAsync(widget.delay.inMilliseconds);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_opacity = 1;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedOpacity(
|
||||
opacity: _opacity,
|
||||
duration: widget.duration,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
22
lib/widget/dialogs/no_files_dialog.dart
Normal file
22
lib/widget/dialogs/no_files_dialog.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/widget/dialogs/custom_bottom_sheet.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
|
||||
class NoFilesDialog extends StatelessWidget {
|
||||
const NoFilesDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomBottomSheet(
|
||||
title: t.dialogs.noFiles.title,
|
||||
description: t.dialogs.noFiles.content,
|
||||
child: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => context.popUntilRoot(),
|
||||
child: Text(t.general.close),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user