TF-248 Change floating button compose email

This commit is contained in:
dab246
2022-02-09 10:49:27 +07:00
committed by Dat H. Pham
parent c0f0b5875b
commit 879692aa54
6 changed files with 200 additions and 4 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.8233 5.22705L14.7725 8.17622L7.58679 15.3629C6.94241 16.0072 6.14959 16.4833 5.27805 16.7494L2.8593 17.4878C2.7124 17.5326 2.55696 17.4499 2.51212 17.303C2.49596 17.2501 2.49596 17.1935 2.51212 17.1406L3.25045 14.7221C3.51657 13.8504 3.99282 13.0575 4.63732 12.413L11.8233 5.22705ZM15.9492 2.86975L17.1311 4.05164C17.5888 4.50925 17.6174 5.23347 17.217 5.72444L17.1311 5.81937L15.6567 7.29288L12.7067 4.34288L14.1814 2.86971C14.6696 2.38163 15.4611 2.38165 15.9492 2.86975Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 608 B

+1
View File
@@ -41,6 +41,7 @@ export 'presentation/views/dialog/loading_dialog_builder.dart';
export 'presentation/views/dialog/downloading_file_dialog_builder.dart';
export 'presentation/views/background/background_widget_builder.dart';
export 'presentation/views/html_viewer/html_content_viewer_widget.dart';
export 'presentation/views/floating_button/scrolling_floating_button_animated.dart';
// Resources
export 'presentation/resources/assets_paths.dart';
@@ -50,6 +50,7 @@ class ImagePaths {
String get icChevronDown => _getImagePath('ic_chevron_down.svg');
String get icFilter => _getImagePath('ic_filter.svg');
String get icSearchBar => _getImagePath('ic_search_bar.svg');
String get icCompose => _getImagePath('ic_compose.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
@@ -0,0 +1,176 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:math' as math;
/// Widget to animate the button when scroll down
class ScrollingFloatingButtonAnimated extends StatefulWidget {
/// Function to use when press the button
final GestureTapCallback? onPress;
/// Double value to set the button elevation
final double? elevation;
/// Double value to set the button width
final double? width;
/// Double value to set the button height
final double? height;
/// Value to set the duration for animation
final Duration? duration;
/// Widget to use as button icon
final Widget? icon;
/// Widget to use as button text when button is expanded
final Widget? text;
/// Value to set the curve for animation
final Curve? curve;
/// ScrollController to use to determine when user is on top or not
final ScrollController? scrollController;
/// Double value to set the boundary value when scroll animation is triggered
final double? limitIndicator;
/// Color to set the button background color
final Color? color;
/// Value to indicate if animate or not the icon
final bool? animateIcon;
ScrollingFloatingButtonAnimated(
{Key? key,
required this.icon,
required this.text,
required this.onPress,
required this.scrollController,
this.elevation = 5.0,
this.width = 120.0,
this.height = 60.0,
this.duration = const Duration(milliseconds: 250),
this.curve,
this.limitIndicator = 10.0,
this.color = Colors.blueAccent,
this.animateIcon = true})
: super(key: key);
@override
_ScrollingFloatingButtonAnimatedState createState() =>
_ScrollingFloatingButtonAnimatedState();
}
class _ScrollingFloatingButtonAnimatedState
extends State<ScrollingFloatingButtonAnimated>
with SingleTickerProviderStateMixin {
/// Boolean value to indicate when scroll view is on top
bool _onTop = true;
/// Value to indicate when to show the child widget
bool _visibleText = false;
/// Controller for icon animation
late AnimationController _animationController;
@override
void initState() {
_animationController = new AnimationController(
vsync: this,
duration: Duration(milliseconds: 250),
reverseDuration: Duration(milliseconds: 250),
animationBehavior: AnimationBehavior.normal,
lowerBound: 0,
upperBound: 120,
);
super.initState();
_handleScroll();
}
@override
void dispose() {
widget.scrollController!.removeListener(() {});
_animationController.dispose();
super.dispose();
}
/// Function to add listener for scroll
void _handleScroll() {
ScrollController _scrollController = widget.scrollController!;
_scrollController.addListener(() {
print(_scrollController.position.userScrollDirection);
if (_scrollController.position.pixels > widget.limitIndicator! &&
_scrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (widget.animateIcon!) _animationController.forward();
setState(() {
_onTop = false;
});
} else if (_scrollController.position.pixels <= widget.limitIndicator! &&
_scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
if (widget.animateIcon!) _animationController.reverse();
setState(() {
_onTop = true;
});
}
});
}
@override
Widget build(BuildContext context) {
return Card(
elevation: widget.elevation,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(widget.height! / 2))),
child: AnimatedContainer(
curve: widget.curve ?? Curves.easeInOut,
duration: widget.duration!,
height: widget.height,
width: _onTop ? widget.width : widget.height,
onEnd: () {
setState(() {
_visibleText = !_visibleText;
});
},
decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(widget.height! / 2))),
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(widget.height! / 2)),
onTap: widget.onPress,
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(widget.height! / 2)),
color: widget.color),
child: Row(
mainAxisAlignment: _onTop ? MainAxisAlignment.spaceEvenly : MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(left: 16, right: _onTop ? 10 : 16),
child: AnimatedBuilder(
child: widget.icon!,
animation: _animationController,
builder: (BuildContext context, Widget? _widget) {
return Transform.rotate(
angle: (_animationController.value * 3 * math.pi) / 180,
child: _widget!,
);
}),
),
...(_onTop
? [
Expanded(
child: AnimatedOpacity(
opacity: !_visibleText ? 1 : 0,
duration: const Duration(milliseconds: 100),
child: widget.text!,
),
)
]
: []),
],
),
),
),
),
);
}
}
@@ -68,11 +68,20 @@ class ThreadView extends GetWidget<ThreadController> {
)
),
floatingActionButton: Obx(() => !controller.isSearchActive()
? FloatingActionButton(
? ScrollingFloatingButtonAnimated(
icon: SvgPicture.asset(imagePaths.icCompose, width: 20, height: 20, fit: BoxFit.fill),
text: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
AppLocalizations.of(context).compose,
style: TextStyle(color: AppColor.colorTextButton, fontSize: 15.0, fontWeight: FontWeight.w500))
),
onPress: () => controller.composeEmailAction(),
scrollController: controller.listEmailController,
color: Colors.white,
elevation: 4.0,
child: new Icon(Icons.add),
backgroundColor: AppColor.appColor,
onPressed: () => controller.composeEmailAction())
width: 140,
animateIcon: false)
: SizedBox.shrink())
),
);
@@ -470,5 +470,11 @@ class AppLocalizations {
'Search for emails and files',
name: 'hint_search_emails');
}
String get compose {
return Intl.message(
'Compose',
name: 'compose');
}
}