Last active
November 24, 2021 22:19
-
-
Save morphingcoffee/10c423dabe89945c373db606b661f2d7 to your computer and use it in GitHub Desktop.
Intro Slider rebuild issue workaround. Issue: https://github.com/duytq94/flutter-intro-slider/issues/59
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:io'; | |
import 'package:flutter/cupertino.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:intro_slider/dot_animation_enum.dart'; | |
import 'package:intro_slider/list_rtl_language.dart'; | |
import 'package:intro_slider/scrollbar_behavior_enum.dart'; | |
import 'package:intro_slider/slide_object.dart'; | |
/// Related issue: https://github.com/duytq94/flutter-intro-slider/issues/59 | |
/// | |
/// Added an optional 'key' constructor parameter so users can pass in a | |
/// different value when they want Flutter to rebuild this from scratch. | |
/// | |
/// Only useful for users wanting to do this on the 0th [Slide], as doing it | |
/// later in the sequence will lose user's progress and jump back to 0th [Slide]. | |
/// | |
/// Example: | |
/// Widget buildSlider() { | |
/// return MCoffeeIntroSlider( | |
/// slides: [ | |
/// ... | |
/// ], | |
/// key: ValueKey(currentTheme.hashCode), | |
/// // TODO configure some theme-related params | |
/// ); | |
/// } | |
/// ... | |
/// void updateTheme() { | |
/// setState((){ | |
/// currentTheme = // TODO some new theme value | |
/// // This will trigger a from-scratch rebuild of IntroSlider | |
/// }); | |
/// } | |
/// | |
class MCoffeeIntroSlider extends StatefulWidget { | |
// ---------- Slides ---------- | |
/// An array of Slide object | |
final List<Slide>? slides; | |
/// Background color for all slides | |
final Color? backgroundColorAllSlides; | |
// ---------- SKIP button ---------- | |
/// Render your own widget SKIP button | |
final Widget? renderSkipBtn; | |
/// Render your own style SKIP button | |
final ButtonStyle? skipButtonStyle; | |
/// Fire when press SKIP button | |
final void Function()? onSkipPress; | |
/// Show or hide SKIP button | |
final bool? showSkipBtn; | |
/// Assign Key to SKIP button | |
final Key? skipButtonKey; | |
// ---------- PREV button ---------- | |
/// Render your own widget PREV button | |
final Widget? renderPrevBtn; | |
/// Render your own style PREV button | |
final ButtonStyle? prevButtonStyle; | |
/// Show or hide PREV button (only visible if skip is hidden) | |
final bool? showPrevBtn; | |
/// Assign Key to PREV button | |
final Key? prevButtonKey; | |
// ---------- NEXT button ---------- | |
/// Render your own widget NEXT button | |
final Widget? renderNextBtn; | |
/// Render your own style NEXT button | |
final ButtonStyle? nextButtonStyle; | |
/// Show or hide NEXT button | |
final bool? showNextBtn; | |
/// Assign Key to NEXT button | |
final Key? nextButtonKey; | |
// ---------- DONE button ---------- | |
/// Render your own widget DONE button | |
final Widget? renderDoneBtn; | |
/// Render your own style NEXT button | |
final ButtonStyle? doneButtonStyle; | |
/// Fire when press DONE button | |
final void Function()? onDonePress; | |
/// Show or hide DONE button | |
final bool? showDoneBtn; | |
/// Assign Key to DONE button | |
final Key? doneButtonKey; | |
// ---------- Dot indicator ---------- | |
/// Show or hide dot indicator | |
final bool? showDotIndicator; | |
/// Color for dot when passive | |
final Color? colorDot; | |
/// Color for dot when active | |
final Color? colorActiveDot; | |
/// Size of each dot | |
final double? sizeDot; | |
/// Type dots animation | |
final dotSliderAnimation? typeDotAnimation; | |
// ---------- Tabs ---------- | |
/// Render your own custom tabs | |
final List<Widget>? listCustomTabs; | |
/// Notify when tab change completed | |
final void Function(int index)? onTabChangeCompleted; | |
/// Ref function go to specific tab index | |
final void Function(Function function)? refFuncGoToTab; | |
// ---------- Behavior ---------- | |
/// Whether or not the slider is scrollable (or controlled only by buttons) | |
final bool? isScrollable; | |
final ScrollPhysics? scrollPhysics; | |
/// Show or hide status bar | |
final bool? hideStatusBar; | |
/// The way the vertical scrollbar should behave | |
final scrollbarBehavior? verticalScrollbarBehavior; | |
// Constructor | |
MCoffeeIntroSlider({ | |
// Slides | |
this.slides, | |
this.backgroundColorAllSlides, | |
// Skip | |
this.renderSkipBtn, | |
this.skipButtonStyle, | |
this.onSkipPress, | |
this.showSkipBtn, | |
this.skipButtonKey, | |
// Prev | |
this.renderPrevBtn, | |
this.prevButtonStyle, | |
this.showPrevBtn, | |
this.prevButtonKey, | |
// Done | |
this.renderDoneBtn, | |
this.onDonePress, | |
this.doneButtonStyle, | |
this.showDoneBtn, | |
this.doneButtonKey, | |
// Next | |
this.renderNextBtn, | |
this.nextButtonStyle, | |
this.showNextBtn, | |
this.nextButtonKey, | |
// Dots | |
this.colorActiveDot, | |
this.colorDot, | |
this.showDotIndicator, | |
this.sizeDot, | |
this.typeDotAnimation, | |
// Tabs | |
this.listCustomTabs, | |
this.onTabChangeCompleted, | |
this.refFuncGoToTab, | |
// Behavior | |
this.isScrollable, | |
this.scrollPhysics, | |
this.hideStatusBar, | |
this.verticalScrollbarBehavior, | |
// Custom Key | |
Key? key, | |
}) : super(key: key); | |
@override | |
MCoffeeIntroSliderState createState() => MCoffeeIntroSliderState(); | |
} | |
class MCoffeeIntroSliderState extends State<MCoffeeIntroSlider> | |
with SingleTickerProviderStateMixin { | |
// ---------- Slides ---------- | |
/// An array of Slide object | |
late final List<Slide>? slides; | |
// ---------- SKIP button ---------- | |
/// Render your own widget SKIP button | |
late final Widget renderSkipBtn; | |
/// Fire when press SKIP button | |
late final void Function()? onSkipPress; | |
/// Render your own style SKIP button | |
late final ButtonStyle skipButtonStyle; | |
/// Show or hide SKIP button | |
late final bool showSkipBtn; | |
/// Assign Key to SKIP button | |
late final Key? skipButtonKey; | |
// ---------- PREV button ---------- | |
/// Render your own widget PREV button | |
late final Widget renderPrevBtn; | |
/// Render your own style PREV button | |
late final ButtonStyle prevButtonStyle; | |
/// Show or hide PREV button | |
late final bool showPrevBtn; | |
/// Assign Key to PREV button | |
late final Key? prevButtonKey; | |
// ---------- DONE button ---------- | |
/// Render your own widget DONE button | |
late final Widget renderDoneBtn; | |
/// Fire when press DONE button | |
late final void Function()? onDonePress; | |
/// Render your own style DONE button | |
late final ButtonStyle doneButtonStyle; | |
/// Show or hide DONE button | |
late final bool showDoneBtn; | |
/// Assign Key to DONE button | |
late final Key? doneButtonKey; | |
// ---------- NEXT button ---------- | |
/// Render your own widget NEXT button | |
late final Widget renderNextBtn; | |
/// Render your own style NEXT button | |
late final ButtonStyle nextButtonStyle; | |
/// Show or hide NEXT button | |
late final bool showNextBtn; | |
/// Assign Key to NEXT button | |
late final Key? nextButtonKey; | |
// ---------- Dot indicator ---------- | |
/// Show or hide dot indicator | |
late final bool showDotIndicator; | |
/// Color for dot when passive | |
late final Color colorDot; | |
/// Color for dot when active | |
late final Color colorActiveDot; | |
/// Size of each dot | |
late final double sizeDot; | |
/// Type dots animation | |
late final dotSliderAnimation typeDotAnimation; | |
// ---------- Tabs ---------- | |
/// List custom tabs | |
List<Widget>? listCustomTabs; | |
/// Notify when tab change completed | |
Function? onTabChangeCompleted; | |
// ---------- Behavior ---------- | |
/// Allow the slider to scroll | |
late final bool isScrollable; | |
late final ScrollPhysics scrollPhysics; | |
/// The way the vertical scrollbar should behave | |
late final scrollbarBehavior verticalScrollbarBehavior; | |
late TabController tabController; | |
List<Widget> tabs = []; | |
List<Widget> dots = []; | |
List<double> sizeDots = []; | |
List<double> opacityDots = []; | |
List<ScrollController> scrollControllers = []; | |
// For DOT_MOVEMENT | |
double marginLeftDotFocused = 0; | |
double marginRightDotFocused = 0; | |
// For SIZE_TRANSITION | |
double currentAnimationValue = 0; | |
int currentTabIndex = 0; | |
late final int lengthSlide; | |
@override | |
void initState() { | |
super.initState(); | |
slides = widget.slides; | |
skipButtonKey = widget.skipButtonKey; | |
prevButtonKey = widget.prevButtonKey; | |
doneButtonKey = widget.doneButtonKey; | |
nextButtonKey = widget.nextButtonKey; | |
lengthSlide = slides?.length ?? widget.listCustomTabs?.length ?? 0; | |
onTabChangeCompleted = widget.onTabChangeCompleted; | |
tabController = TabController(length: lengthSlide, vsync: this); | |
tabController.addListener(() { | |
if (tabController.indexIsChanging) { | |
currentTabIndex = tabController.previousIndex; | |
} else { | |
currentTabIndex = tabController.index; | |
onTabChangeCompleted?.call(tabController.index); | |
} | |
currentAnimationValue = tabController.animation?.value ?? 0; | |
}); | |
// Send reference function goToTab to parent | |
widget.refFuncGoToTab?.call(goToTab); | |
// Dot animation | |
sizeDot = widget.sizeDot ?? 8.0; | |
final initValueMarginRight = (sizeDot * 2) * (lengthSlide - 1); | |
typeDotAnimation = | |
widget.typeDotAnimation ?? dotSliderAnimation.DOT_MOVEMENT; | |
switch (typeDotAnimation) { | |
case dotSliderAnimation.DOT_MOVEMENT: | |
for (var i = 0; i < lengthSlide; i++) { | |
sizeDots.add(sizeDot); | |
opacityDots.add(1.0); | |
} | |
marginRightDotFocused = initValueMarginRight; | |
break; | |
case dotSliderAnimation.SIZE_TRANSITION: | |
for (var i = 0; i < lengthSlide; i++) { | |
if (i == 0) { | |
sizeDots.add(sizeDot * 1.5); | |
opacityDots.add(1.0); | |
} else { | |
sizeDots.add(sizeDot); | |
opacityDots.add(0.5); | |
} | |
} | |
} | |
tabController.animation?.addListener(() { | |
setState(() { | |
switch (typeDotAnimation) { | |
case dotSliderAnimation.DOT_MOVEMENT: | |
marginLeftDotFocused = tabController.animation!.value * sizeDot * 2; | |
marginRightDotFocused = initValueMarginRight - | |
tabController.animation!.value * sizeDot * 2; | |
break; | |
case dotSliderAnimation.SIZE_TRANSITION: | |
if (tabController.animation!.value == currentAnimationValue) { | |
break; | |
} | |
var diffValueAnimation = | |
(tabController.animation!.value - currentAnimationValue).abs(); | |
final diffValueIndex = | |
(currentTabIndex - tabController.index).abs(); | |
// When press skip button | |
if (tabController.indexIsChanging && | |
(tabController.index - tabController.previousIndex).abs() > 1) { | |
if (diffValueAnimation < 1.0) { | |
diffValueAnimation = 1.0; | |
} | |
sizeDots[currentTabIndex] = sizeDot * 1.5 - | |
(sizeDot / 2) * (1 - (diffValueIndex - diffValueAnimation)); | |
sizeDots[tabController.index] = sizeDot + | |
(sizeDot / 2) * (1 - (diffValueIndex - diffValueAnimation)); | |
opacityDots[currentTabIndex] = | |
1.0 - (diffValueAnimation / diffValueIndex) / 2; | |
opacityDots[tabController.index] = | |
0.5 + (diffValueAnimation / diffValueIndex) / 2; | |
} else { | |
if (tabController.animation!.value > currentAnimationValue) { | |
// Swipe left | |
sizeDots[currentTabIndex] = | |
sizeDot * 1.5 - (sizeDot / 2) * diffValueAnimation; | |
sizeDots[currentTabIndex + 1] = | |
sizeDot + (sizeDot / 2) * diffValueAnimation; | |
opacityDots[currentTabIndex] = 1.0 - diffValueAnimation / 2; | |
opacityDots[currentTabIndex + 1] = 0.5 + diffValueAnimation / 2; | |
} else { | |
// Swipe right | |
sizeDots[currentTabIndex] = | |
sizeDot * 1.5 - (sizeDot / 2) * diffValueAnimation; | |
sizeDots[currentTabIndex - 1] = | |
sizeDot + (sizeDot / 2) * diffValueAnimation; | |
opacityDots[currentTabIndex] = 1.0 - diffValueAnimation / 2; | |
opacityDots[currentTabIndex - 1] = 0.5 + diffValueAnimation / 2; | |
} | |
} | |
break; | |
} | |
}); | |
}); | |
// Dot indicator | |
showDotIndicator = widget.showDotIndicator ?? true; | |
colorDot = widget.colorDot ?? const Color(0x80000000); | |
colorActiveDot = widget.colorActiveDot ?? colorDot; | |
isScrollable = widget.isScrollable ?? true; | |
scrollPhysics = widget.scrollPhysics ?? const ScrollPhysics(); | |
verticalScrollbarBehavior = | |
widget.verticalScrollbarBehavior ?? scrollbarBehavior.HIDE; | |
setupButtonDefaultValues(); | |
if (widget.listCustomTabs == null) { | |
renderListTabs(); | |
} else { | |
tabs = widget.listCustomTabs!; | |
} | |
} | |
void setupButtonDefaultValues() { | |
// Skip button | |
onSkipPress = widget.onSkipPress ?? | |
() { | |
if (!isAnimating()) { | |
if (lengthSlide > 0) { | |
tabController.animateTo(lengthSlide - 1); | |
} | |
} | |
}; | |
showSkipBtn = widget.showSkipBtn ?? true; | |
renderSkipBtn = widget.renderSkipBtn ?? | |
Text( | |
"SKIP", | |
style: TextStyle(color: Colors.white), | |
); | |
skipButtonStyle = widget.skipButtonStyle ?? ButtonStyle(); | |
// Prev button | |
if (showSkipBtn) { | |
showPrevBtn = false; | |
} else { | |
showPrevBtn = widget.showPrevBtn ?? true; | |
} | |
renderPrevBtn = widget.renderPrevBtn ?? | |
Text( | |
"PREV", | |
style: TextStyle(color: Colors.white), | |
); | |
prevButtonStyle = widget.prevButtonStyle ?? ButtonStyle(); | |
showNextBtn = widget.showNextBtn ?? true; | |
// Done button | |
onDonePress = widget.onDonePress ?? () {}; | |
renderDoneBtn = widget.renderDoneBtn ?? | |
Text( | |
"DONE", | |
style: TextStyle(color: Colors.white), | |
); | |
doneButtonStyle = widget.doneButtonStyle ?? ButtonStyle(); | |
showDoneBtn = widget.showDoneBtn ?? true; | |
// Next button | |
renderNextBtn = widget.renderNextBtn ?? | |
Text( | |
"NEXT", | |
style: TextStyle(color: Colors.white), | |
); | |
nextButtonStyle = widget.nextButtonStyle ?? ButtonStyle(); | |
} | |
void goToTab(int index) { | |
if (index < tabController.length) { | |
tabController.animateTo(index); | |
} | |
} | |
@override | |
void dispose() { | |
super.dispose(); | |
tabController.dispose(); | |
} | |
// Checking if tab is animating | |
bool isAnimating() { | |
Animation<double>? animation = tabController.animation; | |
if (animation != null) { | |
return animation.value - animation.value.truncate() != 0; | |
} else { | |
return false; | |
} | |
} | |
bool isRTLLanguage(String language) { | |
return rtlLanguages.contains(language); | |
} | |
@override | |
Widget build(BuildContext context) { | |
// Full screen view | |
if (widget.hideStatusBar == true) { | |
SystemChrome.setEnabledSystemUIOverlays([]); | |
} | |
return Scaffold( | |
body: DefaultTabController( | |
length: lengthSlide, | |
child: Stack( | |
children: <Widget>[ | |
TabBarView( | |
controller: tabController, | |
physics: isScrollable | |
? scrollPhysics | |
: const NeverScrollableScrollPhysics(), | |
children: tabs, | |
), | |
renderBottom(), | |
], | |
), | |
), | |
backgroundColor: widget.backgroundColorAllSlides ?? Colors.transparent, | |
); | |
} | |
Widget buildSkipButton() { | |
if (tabController.index + 1 == lengthSlide) { | |
return Container(width: MediaQuery.of(context).size.width / 4); | |
} else { | |
return TextButton( | |
key: skipButtonKey, | |
onPressed: onSkipPress, | |
style: skipButtonStyle, | |
child: renderSkipBtn, | |
); | |
} | |
} | |
Widget buildDoneButton() { | |
return TextButton( | |
key: doneButtonKey, | |
onPressed: onDonePress, | |
style: doneButtonStyle, | |
child: renderDoneBtn, | |
); | |
} | |
Widget buildPrevButton() { | |
if (tabController.index == 0) { | |
return Container(width: MediaQuery.of(context).size.width / 4); | |
} else { | |
return TextButton( | |
key: prevButtonKey, | |
onPressed: () { | |
if (!isAnimating()) { | |
tabController.animateTo(tabController.index - 1); | |
} | |
}, | |
style: prevButtonStyle, | |
child: renderPrevBtn, | |
); | |
} | |
} | |
Widget buildNextButton() { | |
return TextButton( | |
key: nextButtonKey, | |
onPressed: () { | |
if (!isAnimating()) { | |
tabController.animateTo(tabController.index + 1); | |
} | |
}, | |
style: nextButtonStyle, | |
child: renderNextBtn, | |
); | |
} | |
Widget renderBottom() { | |
return Positioned( | |
bottom: 10.0, | |
left: 10.0, | |
right: 10.0, | |
child: Row( | |
children: <Widget>[ | |
// Skip button | |
Container( | |
alignment: Alignment.center, | |
width: MediaQuery.of(context).size.width / 4, | |
child: showSkipBtn | |
? buildSkipButton() | |
: (showPrevBtn ? buildPrevButton() : Container()), | |
), | |
// Dot indicator | |
Flexible( | |
child: showDotIndicator | |
? Stack( | |
children: <Widget>[ | |
Row( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: renderListDots(), | |
), | |
if (typeDotAnimation == dotSliderAnimation.DOT_MOVEMENT) | |
Center( | |
child: Container( | |
decoration: BoxDecoration( | |
color: colorActiveDot, | |
borderRadius: | |
BorderRadius.circular(sizeDot / 2)), | |
width: sizeDot, | |
height: sizeDot, | |
margin: EdgeInsets.only( | |
left: isRTLLanguage( | |
Localizations.localeOf(context) | |
.languageCode) | |
? marginRightDotFocused | |
: marginLeftDotFocused, | |
right: isRTLLanguage( | |
Localizations.localeOf(context) | |
.languageCode) | |
? marginLeftDotFocused | |
: marginRightDotFocused), | |
), | |
) | |
else | |
Container() | |
], | |
) | |
: Container(), | |
), | |
// Next, Done button | |
Container( | |
alignment: Alignment.center, | |
width: MediaQuery.of(context).size.width / 4, | |
height: 50, | |
child: tabController.index + 1 == lengthSlide | |
? showDoneBtn | |
? buildDoneButton() | |
: Container() | |
: showNextBtn | |
? buildNextButton() | |
: Container(), | |
), | |
], | |
), | |
); | |
} | |
List<Widget>? renderListTabs() { | |
for (var i = 0; i < lengthSlide; i++) { | |
final scrollController = ScrollController(); | |
scrollControllers.add(scrollController); | |
tabs.add( | |
renderTab( | |
scrollController, | |
slides?[i].widgetTitle, | |
slides?[i].title, | |
slides?[i].maxLineTitle, | |
slides?[i].styleTitle, | |
slides?[i].marginTitle, | |
slides?[i].widgetDescription, | |
slides?[i].description, | |
slides?[i].maxLineTextDescription, | |
slides?[i].styleDescription, | |
slides?[i].marginDescription, | |
slides?[i].pathImage, | |
slides?[i].widthImage, | |
slides?[i].heightImage, | |
slides?[i].foregroundImageFit, | |
slides?[i].centerWidget, | |
slides?[i].onCenterItemPress, | |
slides?[i].backgroundColor, | |
slides?[i].colorBegin, | |
slides?[i].colorEnd, | |
slides?[i].directionColorBegin, | |
slides?[i].directionColorEnd, | |
slides?[i].backgroundImage, | |
slides?[i].backgroundImageFit, | |
slides?[i].backgroundOpacity, | |
slides?[i].backgroundOpacityColor, | |
slides?[i].backgroundBlendMode, | |
), | |
); | |
} | |
return tabs; | |
} | |
Widget renderTab( | |
ScrollController scrollController, | |
// Title | |
Widget? widgetTitle, | |
String? title, | |
int? maxLineTitle, | |
TextStyle? styleTitle, | |
EdgeInsets? marginTitle, | |
// Description | |
Widget? widgetDescription, | |
String? description, | |
int? maxLineTextDescription, | |
TextStyle? styleDescription, | |
EdgeInsets? marginDescription, | |
// Image | |
String? pathImage, | |
double? widthImage, | |
double? heightImage, | |
BoxFit? foregroundImageFit, | |
// Center Widget | |
Widget? centerWidget, | |
void Function()? onCenterItemPress, | |
// Background color | |
Color? backgroundColor, | |
Color? colorBegin, | |
Color? colorEnd, | |
AlignmentGeometry? directionColorBegin, | |
AlignmentGeometry? directionColorEnd, | |
// Background image | |
String? backgroundImage, | |
BoxFit? backgroundImageFit, | |
double? backgroundOpacity, | |
Color? backgroundOpacityColor, | |
BlendMode? backgroundBlendMode, | |
) { | |
final listView = ListView( | |
controller: scrollController, | |
children: <Widget>[ | |
Container( | |
// Title | |
margin: marginTitle ?? | |
const EdgeInsets.only( | |
top: 70.0, bottom: 50.0, left: 20.0, right: 20.0), | |
child: widgetTitle ?? | |
Text( | |
title ?? '', | |
style: styleTitle ?? | |
const TextStyle( | |
color: Colors.white, | |
fontWeight: FontWeight.bold, | |
fontSize: 30.0, | |
), | |
maxLines: maxLineTitle ?? 1, | |
textAlign: TextAlign.center, | |
overflow: TextOverflow.ellipsis, | |
), | |
), | |
// Image or Center widget | |
GestureDetector( | |
onTap: onCenterItemPress, | |
child: pathImage != null | |
? Image.asset( | |
pathImage, | |
width: widthImage ?? 200.0, | |
height: heightImage ?? 200.0, | |
fit: foregroundImageFit ?? BoxFit.contain, | |
) | |
: Center(child: centerWidget ?? Container()), | |
), | |
// Description | |
Container( | |
margin: marginDescription ?? | |
const EdgeInsets.fromLTRB(20.0, 50.0, 20.0, 50.0), | |
child: widgetDescription ?? | |
Text( | |
description ?? '', | |
style: styleDescription ?? | |
const TextStyle(color: Colors.white, fontSize: 18.0), | |
textAlign: TextAlign.center, | |
maxLines: maxLineTextDescription ?? 100, | |
overflow: TextOverflow.ellipsis, | |
), | |
), | |
], | |
); | |
return Container( | |
width: double.infinity, | |
height: double.infinity, | |
decoration: backgroundImage != null | |
? BoxDecoration( | |
image: DecorationImage( | |
image: AssetImage(backgroundImage), | |
fit: backgroundImageFit ?? BoxFit.cover, | |
colorFilter: ColorFilter.mode( | |
backgroundOpacityColor != null | |
? backgroundOpacityColor | |
.withOpacity(backgroundOpacity ?? 0.5) | |
: Colors.black.withOpacity(backgroundOpacity ?? 0.5), | |
backgroundBlendMode ?? BlendMode.darken, | |
), | |
), | |
) | |
: BoxDecoration( | |
gradient: LinearGradient( | |
colors: backgroundColor != null | |
? [backgroundColor, backgroundColor] | |
: [ | |
colorBegin ?? Colors.amberAccent, | |
colorEnd ?? Colors.amberAccent | |
], | |
begin: directionColorBegin ?? Alignment.topLeft, | |
end: directionColorEnd ?? Alignment.bottomRight, | |
), | |
), | |
child: Container( | |
margin: const EdgeInsets.only(bottom: 60.0), | |
child: verticalScrollbarBehavior != scrollbarBehavior.HIDE | |
? Platform.isIOS | |
? CupertinoScrollbar( | |
controller: scrollController, | |
isAlwaysShown: verticalScrollbarBehavior == | |
scrollbarBehavior.SHOW_ALWAYS, | |
child: listView, | |
) | |
: Scrollbar( | |
controller: scrollController, | |
isAlwaysShown: verticalScrollbarBehavior == | |
scrollbarBehavior.SHOW_ALWAYS, | |
child: listView, | |
) | |
: listView, | |
), | |
); | |
} | |
List<Widget> renderListDots() { | |
dots.clear(); | |
for (var i = 0; i < lengthSlide; i++) { | |
dots.add(renderDot(sizeDots[i], colorDot, opacityDots[i], i)); | |
} | |
return dots; | |
} | |
Widget renderDot(double radius, Color? color, double opacity, int index) { | |
return GestureDetector( | |
onTap: () { | |
tabController.animateTo(index); | |
}, | |
child: Opacity( | |
opacity: opacity, | |
child: Container( | |
decoration: BoxDecoration( | |
color: color, borderRadius: BorderRadius.circular(radius / 2)), | |
width: radius, | |
height: radius, | |
margin: EdgeInsets.only(left: radius / 2, right: radius / 2), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment