![](/img/trans.png)
[英]flutter RangeError (index): Invalid value: Not in inclusive range 0..4: 5 on CarouselSlider
[英]flutter CarouselSlider is not changing page when moving
我创建了一个 CarouselSlider 试图使其按预期工作但只有图像发生变化,而不是点指示器或标题:
这是我的代码:
屏幕
class OnBoarding extends StatefulWidget {
static var tag = "/ShWalkThroughScreen";
@override
_OnBoardingState createState() => _OnBoardingState();
}
class _OnBoardingState extends State<OnBoarding> {
OnBoardingController _onBoardingController = Get.put(OnBoardingController());
@override
void dispose() {
super.dispose();
changeStatusColor(Colors.white);
}
@override
Widget build(BuildContext context) {
changeStatusColor(Colors.white);
var width = MediaQuery.of(context).size.width;
width = width - 50;
_onBoardingController.gettingWalkThroughData();
return Obx(()=> Scaffold(
body: SafeArea(
child: Container(
height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(spacing_large, spacing_large, spacing_large, spacing_standard_new),
child: Column(
children: <Widget>[
text(_onBoardingController.mHeadingList[_onBoardingController.position.value], textColor: sh_textColorPrimary, fontSize: textSizeLarge, fontFamily: fontBold),
SizedBox(height: 10.0),
text(_onBoardingController.mSubtitle1ingList[_onBoardingController.position.value], fontSize: textSizeLargeMedium, maxLine: 3, isCentered: true),
],
),
),
ShCarouselSlider(
viewportFraction: 0.8,
height: MediaQuery.of(context).size.height * 0.5,
enlargeCenterPage: true,
scrollDirection: Axis.horizontal,
items: _onBoardingController.mSliderList.map((slider) {
return Builder(
builder: (BuildContext context) {
return Container(
width: width * 0.9,
//height: width + width * 0.1,
decoration: BoxDecoration(
color: sh_white,
borderRadius: BorderRadius.all(Radius.circular(spacing_standard)),
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.4), spreadRadius: spacing_control_half, blurRadius: 10, offset: Offset(1, 3))],
),
margin: EdgeInsets.all(spacing_standard_new),
child: Center(
child: CachedNetworkImage(
placeholder: placeholderWidgetFn() as Widget Function(BuildContext, String), imageUrl: slider, width: MediaQuery.of(context).size.width, fit: BoxFit.cover),
),
);
},
);
}).toList(),
onPageChanged: (index) {
_onBoardingController.changeIndex(index);
},
),
Padding(
padding: const EdgeInsets.all(spacing_large),
child: Column(
children: <Widget>[
Obx(()=> DotsIndicator(
dotsCount: 3,
position: _onBoardingController.position.value,
decorator: DotsDecorator(color: sh_view_color, activeColor: sh_colorPrimary, activeSize: const Size.square(14.0), spacing: EdgeInsets.all(spacing_control)),
),
),
SizedBox(height: spacing_standard),
SizedBox(
width: double.infinity,
height: 50,
child: MaterialButton(
padding: EdgeInsets.all(spacing_standard),
child: Text(sh_text_start_to_shopping, style: TextStyle(fontSize: 18)),
textColor: sh_white,
shape: RoundedRectangleBorder(borderRadius: new BorderRadius.circular(40.0)),
color: sh_colorPrimary,
onPressed: () {
finish(context);
ShHomeScreen().launch(context);
},
),
),
SizedBox(height: spacing_standard),
InkWell(
onTap: () {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) {
return T3SignIn();
}));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
text(sh_lbl_already_have_a_account),
text(sh_lbl_sign_in, textColor: sh_textColorPrimary, fontFamily: fontBold),
],
),
)
],
),
),
],
),
),
),
),
),
);
}
}
// ignore: must_be_immutable
class ShSliderWidget extends StatelessWidget {
OnBoardingController _onBoardingController = Get.put(OnBoardingController());
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
width = width - 50;
final Size cardSize = Size(width, width / 1.8);
return ShCarouselSlider(
viewportFraction: 0.9,
height: cardSize.height,
enlargeCenterPage: true,
scrollDirection: Axis.horizontal,
items: _onBoardingController.mSliderList.map((slider) {
return Builder(
builder: (BuildContext context) {
return Obx(()=> Container(
width: MediaQuery.of(context).size.width,
height: cardSize.height,
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
elevation: 0,
margin: EdgeInsets.all(0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
child: CachedNetworkImage(
placeholder: placeholderWidgetFn() as Widget Function(BuildContext, String),
imageUrl: slider,
fit: BoxFit.fill,
width: MediaQuery.of(context).size.width,
height: cardSize.height),
),
),
);
},
);
}).toList(),
onPageChanged: (index) {
_onBoardingController.position.value = index;
},
);
}
}
小工具
class ShCarouselSlider extends StatefulWidget {
ShCarouselSlider(
{@required List<Widget> this.items,
this.height,
this.aspectRatio: 16 / 9,
this.viewportFraction: 0.8,
this.initialPage: 0,
int realPage: 10000,
this.enableInfiniteScroll: true,
this.reverse: false,
this.autoPlay: false,
this.autoPlayInterval: const Duration(seconds: 4),
this.autoPlayAnimationDuration = const Duration(milliseconds: 800),
this.autoPlayCurve: Curves.fastOutSlowIn,
this.pauseAutoPlayOnTouch,
this.enlargeCenterPage = false,
this.onPageChanged,
this.scrollPhysics,
this.scrollDirection: Axis.horizontal})
: this.realPage = enableInfiniteScroll ? realPage + initialPage : initialPage,
this.itemCount = items.length,
this.itemBuilder = null,
this.pageController = PageController(
viewportFraction: viewportFraction as double,
initialPage: enableInfiniteScroll ? realPage + (initialPage as int) : initialPage as int,
);
/// The on demand item builder constructor
ShCarouselSlider.builder(
{@required this.itemCount,
@required this.itemBuilder,
this.height,
this.aspectRatio: 16 / 9,
this.viewportFraction: 0.8,
this.initialPage: 0,
int realPage: 10000,
this.enableInfiniteScroll: true,
this.reverse: false,
this.autoPlay: false,
this.autoPlayInterval: const Duration(seconds: 4),
this.autoPlayAnimationDuration = const Duration(milliseconds: 800),
this.autoPlayCurve: Curves.fastOutSlowIn,
this.pauseAutoPlayOnTouch,
this.enlargeCenterPage = false,
this.onPageChanged,
this.scrollPhysics,
this.scrollDirection: Axis.horizontal})
: this.realPage = enableInfiniteScroll ? realPage + initialPage : initialPage,
this.items = null,
this.pageController = PageController(
viewportFraction: viewportFraction as double,
initialPage: enableInfiniteScroll ? realPage + (initialPage as int) : initialPage as int,
);
/// The widgets to be shown in the carousel of default constructor
final List<Widget> items;
/// The widget item builder that will be used to build item on demand
final IndexedWidgetBuilder itemBuilder;
/// The widgets count that should be shown at carousel
final int itemCount;
/// Set carousel height and overrides any existing [aspectRatio].
final double height;
/// Aspect ratio is used if no height have been declared.
///
/// Defaults to 16:9 aspect ratio.
final double aspectRatio;
/// The fraction of the viewport that each page should occupy.
///
/// Defaults to 0.8, which means each page fills 80% of the carousel.
final num viewportFraction;
/// The initial page to show when first creating the [ShCarouselSlider].
///
/// Defaults to 0.
final num initialPage;
/// The actual index of the [PageView].
///
/// This value can be ignored unless you know the carousel will be scrolled
/// backwards more then 10000 pages.
/// Defaults to 10000 to simulate infinite backwards scrolling.
final num realPage;
///Determines if carousel should loop infinitely or be limited to item length.
///
///Defaults to true, i.e. infinite loop.
final bool enableInfiniteScroll;
/// Reverse the order of items if set to true.
///
/// Defaults to false.
final bool reverse;
/// Enables auto play, sliding one page at a time.
///
/// Use [autoPlayInterval] to determent the frequency of slides.
/// Defaults to false.
final bool autoPlay;
/// Sets Duration to determent the frequency of slides when
///
/// [autoPlay] is set to true.
/// Defaults to 4 seconds.
final Duration autoPlayInterval;
/// The animation duration between two transitioning pages while in auto playback.
///
/// Defaults to 800 ms.
final Duration autoPlayAnimationDuration;
/// Determines the animation curve physics.
///
/// Defaults to [Curves.fastOutSlowIn].
final Curve autoPlayCurve;
/// Sets a timer on touch detected that pause the auto play with
/// the given [Duration].
///
/// Touch Detection is only active if [autoPlay] is true.
final Duration pauseAutoPlayOnTouch;
/// Determines if current page should be larger then the side images,
/// creating a feeling of depth in the carousel.
///
/// Defaults to false.
final bool enlargeCenterPage;
/// The axis along which the page view scrolls.
///
/// Defaults to [Axis.horizontal].
final Axis scrollDirection;
/// Called whenever the page in the center of the viewport changes.
final Function(int index) onPageChanged;
/// How the carousel should respond to user input.
///
/// For example, determines how the items continues to animate after the
/// user stops dragging the page view.
///
/// The physics are modified to snap to page boundaries using
/// [PageScrollPhysics] prior to being used.
///
/// Defaults to matching platform conventions.
final ScrollPhysics scrollPhysics;
/// [pageController] is created using the properties passed to the constructor
/// and can be used to control the [PageView] it is passed to.
final PageController pageController;
/// Animates the controlled [ShCarouselSlider] to the next page.
///
/// The animation lasts for the given duration and follows the given curve.
/// The returned [Future] resolves when the animation completes.
Future<void> nextPage({@required Duration duration, @required Curve curve}) {
return pageController.nextPage(duration: duration, curve: curve);
}
/// Animates the controlled [ShCarouselSlider] to the previous page.
///
/// The animation lasts for the given duration and follows the given curve.
/// The returned [Future] resolves when the animation completes.
Future<void> previousPage({@required Duration duration, @required Curve curve}) {
return pageController.previousPage(duration: duration, curve: curve);
}
/// Changes which page is displayed in the controlled [ShCarouselSlider].
///
/// Jumps the page position from its current value to the given value,
/// without animation, and without checking if the new value is in range.
void jumpToPage(int page) {
final index = _getRealIndex(pageController.page.toInt(), realPage - initialPage as int, itemCount);
return pageController.jumpToPage(pageController.page.toInt() + page - index);
}
/// Animates the controlled [ShCarouselSlider] from the current page to the given page.
///
/// The animation lasts for the given duration and follows the given curve.
/// The returned [Future] resolves when the animation completes.
Future<void> animateToPage(int page, {@required Duration duration, @required Curve curve}) {
final index = _getRealIndex(pageController.page.toInt(), realPage - initialPage as int, itemCount);
return pageController.animateToPage(pageController.page.toInt() + page - index, duration: duration, curve: curve);
}
@override
_ShCarouselSliderState createState() => _ShCarouselSliderState();
}
class _ShCarouselSliderState extends State<ShCarouselSlider> with TickerProviderStateMixin {
Timer timer;
@override
void initState() {
super.initState();
timer = getTimer();
}
Timer getTimer() {
return widget.autoPlay
? Timer.periodic(widget.autoPlayInterval, (_) {
widget.pageController.nextPage(duration: widget.autoPlayAnimationDuration, curve: widget.autoPlayCurve);
})
: null;
}
void pauseOnTouch() {
timer.cancel();
timer = Timer(widget.pauseAutoPlayOnTouch, () {
timer = getTimer();
});
}
Widget getWrapper(Widget child) {
if (widget.height != null) {
final Widget wrapper = Container(height: widget.height, child: child);
return widget.autoPlay && widget.pauseAutoPlayOnTouch != null ? addGestureDetection(wrapper) : wrapper;
} else {
final Widget wrapper = AspectRatio(aspectRatio: widget.aspectRatio, child: child);
return widget.autoPlay && widget.pauseAutoPlayOnTouch != null ? addGestureDetection(wrapper) : wrapper;
}
}
Widget addGestureDetection(Widget child) => GestureDetector(onPanDown: (_) => pauseOnTouch(), child: child);
@override
void dispose() {
super.dispose();
timer?.cancel();
}
@override
Widget build(BuildContext context) {
return getWrapper(CarouselSlider(
options: CarouselOptions(
enlargeCenterPage: true,
viewportFraction: 0.8,
),
items: widget.items.map((i) {
return Container(
child: i,
);
}).toList(),
));
}
}
/// Converts an index of a set size to the corresponding index of a collection of another size
/// as if they were circular.
///
/// Takes a [position] from collection Foo, a [base] from where Foo's index originated
/// and the [length] of a second collection Baa, for which the correlating index is sought.
///
/// For example; We have a Carousel of 10000(simulating infinity) but only 6 images.
/// We need to repeat the images to give the illusion of a never ending stream.
/// By calling _getRealIndex with position and base we get an offset.
/// This offset modulo our length, 6, will return a number between 0 and 5, which represent the image
/// to be placed in the given position.
int _getRealIndex(int position, int base, int length) {
final int offset = position - base;
return _remainder(offset, length);
}
/// Returns the remainder of the modulo operation [input] % [source], and adjust it for
/// negative values.
int _remainder(int input, int source) {
if (source == 0) return 0;
final int result = input % source;
return result < 0 ? source + result : result;
}
Controller
class OnBoardingController extends GetxController {
var onBoardingLoading = false.obs;
var position = 0.obs;
var mSliderList = <String>[].obs;
var mHeadingList = <String>["Hi, Welcome", "Most Unique Styles!", "Shop Till You Drop!"].obs;
var mSubtitle1ingList = <String>[
"We make around your city Affordable,easy and efficient.",
"Shop the most trending fashion on the biggest shopping website",
"Grab the best seller pieces at bargain prices."
];
gettingWalkThroughData() {
onBoardingLoading.value = true;
ApiServices().getWalkTrough().then((resp) {
onBoardingLoading.value = false;
mSliderList.clear();
mHeadingList.clear();
mSubtitle1ingList.clear();
for (var item in resp['data']){
mSliderList.add("${Constants.IMAGE_BASE_URL}${item['image']}/");
mHeadingList.add(item['title']);
mSubtitle1ingList.add(item['sub_title']);
}
});
}
changeIndex(int index){
position.value = index;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.