diff --git a/lib/components/animation/gf_animation.dart b/lib/components/animation/gf_animation.dart index 27959dc8..15752758 100644 --- a/lib/components/animation/gf_animation.dart +++ b/lib/components/animation/gf_animation.dart @@ -31,11 +31,15 @@ class GFAnimation extends StatefulWidget { this.fontWeight, this.changedWidth, this.changedHeight, + this.reverseDuration, }) : super(key: key); /// The duration for animations of the [Decoration]. final Duration duration; + /// The duration for animations of the type[Size]. + final Duration reverseDuration; + /// Defines how the animated widget is aligned within the Animation. final Alignment alignment; @@ -203,8 +207,8 @@ class _GFAnimationState extends State child: AnimatedAlign( curve: widget.curve ?? Curves.linear, alignment: selected - ? widget.alignment ?? Alignment.center - : Alignment.topCenter, + ? widget.activeAlignment ?? Alignment.center + : widget.alignment ?? Alignment.topCenter, duration: widget.duration ?? const Duration(seconds: 2), child: widget.child, ), @@ -212,18 +216,31 @@ class _GFAnimationState extends State ); Widget buildAnimatedSizeWidget() => GestureDetector( - onTap: widget.onTap, - child: Container( - margin: widget.margin ?? const EdgeInsets.all(0), - padding: widget.padding ?? const EdgeInsets.all(0), - color: widget.color ?? Colors.white, - child: AnimatedSize( - alignment: widget.alignment ?? Alignment.center, - curve: widget.curve ?? Curves.linear, - vsync: this, - duration: widget.duration ?? const Duration(milliseconds: 2000), - child: widget.child, - ), + onTap: () { + if (widget.onTap == null) { + if (mounted) { + setState(() { + selected = !selected; + }); + } + } else { + widget.onTap(); + } + }, + child: AnimatedSize( + alignment: widget.alignment ?? Alignment.center, + curve: widget.curve ?? Curves.linear, + vsync: this, + reverseDuration: + widget.reverseDuration ?? const Duration(milliseconds: 2000), + duration: widget.duration ?? const Duration(milliseconds: 2000), + child: Container( + margin: widget.margin ?? const EdgeInsets.all(0), + padding: widget.padding ?? const EdgeInsets.all(0), + color: widget.color ?? Colors.white, + height: selected ? widget.height ?? 200 : widget.height ?? 100, + width: selected ? widget.width ?? 200 : widget.width ?? 100, + child: widget.child), ), ); diff --git a/lib/components/intro_screen/gf__intro_bottom_navigation.dart b/lib/components/intro_screen/gf__intro_bottom_navigation.dart new file mode 100644 index 00000000..c2aa444b --- /dev/null +++ b/lib/components/intro_screen/gf__intro_bottom_navigation.dart @@ -0,0 +1,160 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class GFIntroBottomNavigation extends StatelessWidget { + const GFIntroBottomNavigation({ + Key key, + this.rightText = 'NEXT', + this.pageNumber = 0, + this.onNext, + this.showDivider = true, + this.dividerColor = Colors.grey, + this.dividerHeight = 1, + this.dividerThickness = 0.0, + this.child, + this.padding = const EdgeInsets.all(8), + this.margin = const EdgeInsets.all(8), + this.pagesCount = 0, + this.skipText = 'SKIP', + this.onSkipTap, + this.skipWidget, + this.rightWidget, + this.dotShape = BoxShape.circle, + this.defaultColor, + this.activeColor, + this.dotHeight, + this.dotWidth, + this.dotMargin, + this.skipStyle, + this.rightStyle, + this.onDoneTap, + this.doneText = 'GO', + }) : super(key: key); + + final String rightText; + final int pageNumber; + final VoidCallback onNext; + final bool showDivider; + final double dividerHeight; + final double dividerThickness; + final Color dividerColor; + final Widget child; + final int pagesCount; + final String skipText; + final VoidCallback onSkipTap; + final VoidCallback onDoneTap; + final EdgeInsets padding; + final EdgeInsets margin; + final Widget skipWidget; + final Widget rightWidget; + final TextStyle skipStyle; + final TextStyle rightStyle; + final String doneText; + + ///dot + final BoxShape dotShape; + final Color defaultColor; + final Color activeColor; + final double dotHeight; + final double dotWidth; + final EdgeInsets dotMargin; + + @override + Widget build(BuildContext context) => Container( + child: DefaultTextStyle( + style: const TextStyle( + color: Colors.black, + fontSize: 16, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + showDivider + ? Divider( + height: dividerHeight, + thickness: dividerThickness, + color: dividerColor, + ) + : Container(), + Container( + padding: padding, + margin: margin, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + child: Padding( + padding: const EdgeInsets.only( + top: 8, + bottom: 8, + left: 24, + right: 32, + ), + child: skipWidget ?? + Text( + skipText, + style: skipStyle ?? + const TextStyle( + color: Colors.black, + fontSize: 16, + ), + )), + onTap: onSkipTap, + ), + Expanded( + child: Container( + child: Stack( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: getDotsList(), + ) + ], + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + child: Padding( + padding: const EdgeInsets.only( + top: 8, bottom: 8, left: 32, right: 24), + child: rightWidget ?? + Text( + pageNumber == pagesCount - 1 + ? doneText + : rightText, + style: rightStyle ?? + const TextStyle( + color: Colors.black, + fontSize: 16, + )), + ), + onTap: pageNumber == pagesCount - 1 ? onDoneTap : onNext, + ), + ], + ), + ) + ], + ), + ), + ); + + List getDotsList() { + final List list = []; + for (int i = 0; i < pagesCount; i++) { + list.add(Container( + width: dotWidth ?? 12, + height: dotHeight ?? 12, + margin: dotMargin ?? const EdgeInsets.symmetric(horizontal: 4), + decoration: BoxDecoration( + shape: dotShape, + color: pageNumber == i + ? activeColor ?? Colors.blue + : defaultColor ?? Colors.grey.withOpacity(0.5), + ), + )); + } + return list; + } +} diff --git a/lib/components/intro_screen/gf_intro_screen.dart b/lib/components/intro_screen/gf_intro_screen.dart new file mode 100644 index 00000000..a2bcb973 --- /dev/null +++ b/lib/components/intro_screen/gf_intro_screen.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:getwidget/components/intro_screen/gf__intro_bottom_navigation.dart'; +import 'package:getwidget/components/intro_screen/gf_intro_slide.dart'; +import 'package:getwidget/types/gf_intro_type.dart'; + +class GFIntroScreen extends StatefulWidget { + const GFIntroScreen( + {Key key, + this.slides, + this.pageController, + this.gfIntroBottomNavigation, + this.type, + this.color = Colors.white}) + : super(key: key); + + /// if the type as [GFIntroType.fullWidth],[GFIntroType.half],[GFIntroType.rounded] use [GFIntroSlide]'s or customWidgets + final List slides; + + /// type of [GFIntroType] which takes the type ie, fullWidth, half,rounded and bubble for the [GFIntroScreen] + final GFIntroType type; + + /// default controller for the [GFIntroScreen] component + final PageController pageController; + + /// [GFIntroScreen] bottom navigation will be used as [GFIntroBottomNavigation] component + final GFIntroBottomNavigation gfIntroBottomNavigation; + + /// background color of the [GFIntroScreen] component + final Color color; + + @override + _GFIntroScreenState createState() => _GFIntroScreenState(); +} + +class _GFIntroScreenState extends State { + PageController _pageController = PageController(initialPage: 0); + int page = 0; + List pages; + + @override + void initState() { + if (widget.pageController != null) { + _pageController = widget.pageController; + } + _pageController.addListener(() { + if (mounted) { + setState(() { + page = _pageController.page.round(); + }); + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) => Center( + child: Container( + width: widget.type == GFIntroType.fullWidth + ? MediaQuery.of(context).size.width + : MediaQuery.of(context).size.width * 0.885, + height: widget.type != GFIntroType.fullWidth + ? MediaQuery.of(context).size.height / 2 + : MediaQuery.of(context).size.height, + margin: widget.type != GFIntroType.fullWidth + ? const EdgeInsets.only(left: 20, right: 20) + : const EdgeInsets.only(left: 0, right: 0), + padding: widget.type == GFIntroType.fullWidth + ? const EdgeInsets.all(0) + : const EdgeInsets.all(0), + decoration: BoxDecoration( + color: widget.color, + borderRadius: widget.type == GFIntroType.fullWidth + ? BorderRadius.circular(0) + : widget.type == GFIntroType.rounded + ? BorderRadius.circular(24) + : BorderRadius.zero, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: ClipRRect( + borderRadius: widget.type == GFIntroType.rounded + ? const BorderRadius.only( + topLeft: Radius.circular(24), + topRight: Radius.circular(24)) + : BorderRadius.zero, + child: PageView( + controller: _pageController, + children: widget.slides, + ), + )), + widget.gfIntroBottomNavigation ?? + GFIntroBottomNavigation( + onNext: () { + _pageController.nextPage( + duration: const Duration(milliseconds: 500), + curve: Curves.linear); + }, + pagesCount: widget.slides.length, + pageNumber: page, + ) + ], + ), + ), + ); +} diff --git a/lib/components/intro_screen/gf_intro_slide.dart b/lib/components/intro_screen/gf_intro_slide.dart new file mode 100644 index 00000000..a084eb7d --- /dev/null +++ b/lib/components/intro_screen/gf_intro_slide.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:getwidget/colors/gf_color.dart'; +import 'package:getwidget/components/image/gf_image_overlay.dart'; + +class GFIntroSlide extends StatelessWidget { + const GFIntroSlide({ + Key key, + @required this.image, + this.imageHeight = 100, + this.imageWidth = 100, + this.title, + this.subTitle, + this.titleStyle = const TextStyle(fontSize: 20, color: GFColors.DARK), + this.subTitleStyle = const TextStyle(fontSize: 16, color: GFColors.DARK), + this.backgroundColor = GFColors.PRIMARY, + }) : super(key: key); + final double imageHeight; + final double imageWidth; + final ImageProvider image; + final String title; + final TextStyle titleStyle; + final String subTitle; + final TextStyle subTitleStyle; + final Color backgroundColor; + + @override + Widget build(BuildContext context) => Container( + color: backgroundColor, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GFImageOverlay( + height: imageHeight, + colorFilter: const ColorFilter.mode(null, null), + width: imageWidth, + image: image), + const SizedBox( + height: 20, + ), + Text( + title ?? 'Title', + style: titleStyle, + ), + const SizedBox( + height: 40, + ), + Text( + subTitle ?? 'Sub Title', + style: subTitleStyle, + ) + ], + ), + ); +} diff --git a/lib/types/gf_intro_type.dart b/lib/types/gf_intro_type.dart new file mode 100644 index 00000000..db854849 --- /dev/null +++ b/lib/types/gf_intro_type.dart @@ -0,0 +1 @@ +enum GFIntroType { fullWidth, half, rounded } diff --git a/pubspec.yaml b/pubspec.yaml index a0a56314..41fbd5a0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,4 +15,4 @@ dev_dependencies: sdk: flutter flutter: - uses-material-design: true + uses-material-design: true \ No newline at end of file