I want to show the all images from the API in the carousel and for items, I want to show 2 items per slide.
and it works correctly what I want. but in every last item, it shows me the error of CarouselSlider. I don't know why it gives me this error. I tried to solve it but failed because I don't know why they give me this error in the last item.
here is my code:-
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
class PersonImages extends StatefulWidget {
PersonImages({Key? key}) : super(key: key);
#override
_PersonImages createState() => _PersonImages();
}
class _PersonImages extends State<PersonImages>{
var UsriD = Auth.prefs?.getString('usrid');
var Imagedata;
var img = "";
var user = "";
#override
void initState() {
super.initState();
getImageData();
}
getImageData() async{
var res = await http.get(Uri.https('www.*******.net', '/index.php',{'act':'usrPhotos','Usrid': '${UsriD}'}));
Imagedata = jsonDecode(res.body);
//print(Imagedata);
setState(() {});
print(res.body);
}
#override
Widget build(BuildContext context) {
//print(Imagedata);
return
Imagedata != null? CarouselSlider.builder(
options: CarouselOptions(
//height: 150,
aspectRatio: 2.0,
enableInfiniteScroll: false,
//viewportFraction: 0.8,
enlargeCenterPage: true,
viewportFraction: 1,
),
itemCount: (Imagedata.length / 2).round(),
//itemCount: 3,
itemBuilder: (BuildContext context, int index, int pageViewIndex) {
final int first = index * 2;
final int second = first + 1;
return
Row(
children: [first, second].map((idx) {
return Expanded(
//flex: 2,
child: Container(
child: Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
child:
Image.network(
"https://www.*******.net/files/images/${Imagedata[idx]['image']}",
fit: BoxFit.cover,
width: 200,
height: 200,
),
),
),
)
);
}).toList(),
);
}
): const Center(
child: CircularProgressIndicator(),
);
}
}
Here is my error and output :-
please help me if anyone knows how to resolve it.
is anyone knows how to do this so answer my question?
In case of an odd number length, for example 5
you divided it by 2. which is 2.5 Rounded it then the value is 3.
Now when it loops for the third time. It will try and fetch 2*2 + 1. now you will try and find the element by id 5 which doesn't exist. You can try
itemCount: (Imagedata.length / 2).floor(),
Note you may lose the last image in case of an odd length.
Try checking null like
Imagedata[idx]['image']!= null?
Image.network("...${Imagedata[idx]['image']}", )
:Text("cant find image index"),
I have created a CarouselSlider trying to make it work as expected but only images changing, not the dots indicator or the titles :
here are my code :
The screen
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;
},
);
}
}
The Widget
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;
}
The 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;
}
}
Hello Super seniors developer. I am trying to make a post maker app like 1SStory in flutter I want to add image or text or icon in stack and then want to make it moveable , zoom in out using 2 finger pinch and rotate .
I need same as given video example
You can check here
I tried many solutions from stackoverflow these cases are below
Case1
I tried below code but it works only for make a widget moveable , I can't pinch and rotate it.
class MoveableStackItem extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MoveableStackItemState();
}
}
class _MoveableStackItemState extends State<MoveableStackItem> {
double xPosition = 0;
double yPosition = 0;
Color color;
double height = 150, width = 150;
#override
void initState() {
color = RandomColor().randomColor();
super.initState();
}
#override
Widget build(BuildContext context) {
return Positioned(
top: yPosition,
left: xPosition,
child: GestureDetector(
onPanUpdate: (tapInfo) {
setState(() {
xPosition += tapInfo.delta.dx;
yPosition += tapInfo.delta.dy;
print(xPosition);
});},
child: Container(
width: xPosition==0 ? width: xPosition,
height: yPosition ==0? height :yPosition,
color: color,),),);}}
Case2
I tried below code matrix_gesture_detector it use stack I can rotate, pinch and move but this is done in stack so when I do any operation like move zoom all widget get moved and zoom at once that it problem if you have any solution please guide me .
Stack(
children: [
MatrixGestureDetector(
onMatrixUpdate: (m, tm, sm, rm) {
notifier.value = m;},
child: AnimatedBuilder(
animation: notifier,
builder: (ctx, child) {
return Transform(
transform: notifier.value,
child: Stack(
children: <Widget>[
Positioned.fill(
child: Container(
height: 150,
width: 150,
transform: notifier.value,
child: Container(height: 150,width: 150,color: Colors.yellow,),
),),],),);},),),],),
I am stuck at from last 5 days still doing experiments butt all in vain please guide me I shall be very thank full to you .
I want to add a button at the end of my GridView. I viewed another similar problem but the code given in the answer does not scroll. Here is the link to that answer.
My design has a similar. Here is a rough sketch.
Also just for clarification, I want the button to appear once the user has scrolled to the end of the grid view.
I am still new to flutter so your help would be much appreciated :)
The thing which you need is ScrollController class.
WHY SCROLLCONTROLLER?
It keeps track of what are we doing with scrolling, that is, scrolling, reached bottom, or top
HOW CAN WE USE IT?
You need to use it inside GridView, to get your things up and running
// Simply initialise your controller in your project
ScrollController _controller = new ScrollController();
// add listener to the controller to find out the scrolling event
_controller.addListener((){});
// pass this into your GridView.
// We we will add it to GridView. ScrollController is for every scrolling widget
// in Flutter
GridView.builder(
controller: _controller,
)
DISCLAIMER: Please do not look at the UI aspect, since we care about the scrolling event tracking and show/hide our button
I have referred to your given link only for creating the UI => Your Provided Link
Also, I have added scrolling event to identify whether we're scrolling or not, but it is commented
The project currently show the button when we reach the bottom, and hide it when we are the top
CODE
class _MyHomePageState extends State<MyHomePage> {
List<String> homeList = [];
//to check whether we have reached bottom
bool isBottom = false;
ScrollController _controller = new ScrollController();
#override
void initState() {
super.initState();
homeList = List.generate(10, (ind) => 'Item $ind').toList();
// adding controller to check whether the page is
// at the bottom
_controller.addListener((){
// reached bottom
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
setState(() => isBottom = true);
}
// IS SCROLLING
// if (_controller.offset >= _controller.position.minScrollExtent &&
// _controller.offset < _controller.position.maxScrollExtent && !_controller.position.outOfRange) {
// setState(() {
// isBottom = false;
// });
// }
// REACHED TOP
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
setState(() => isBottom = false);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
GridView.builder(
shrinkWrap: true,
controller: _controller,
itemCount: homeList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisSpacing: 20),
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () => print(i),
child: Container(
margin: EdgeInsets.only(bottom: 20.0),
decoration: BoxDecoration(
color: Colors.indigo[300],
borderRadius: BorderRadius.circular(15.0)
)
)
);
}
),
// if we are bottom we show the button
// else empty container, which is nothing but
// hiding technique in Flutter
isBottom ? Positioned(
bottom: 20,
left: 18,
right: 18,
child: Container(
alignment: Alignment.center,
height: 50,
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(15),
),
child: Text('Your widget at the end')
)
) : Container()
]
)
)
);
}
}
RESULT
How can we dynamically add drag and drop anywhere widget in flutter?
Here is what I tried to implement. Suggest changes to solve the problem or suggest the proper method to implement it.
The problem I need to solve is that the canvasItemObj.x and canvasItemObj.y variables are updating according to t the dragUpdate. But the position of the widget is not updating. (top and left parameters of the Positioned() widget)
Here is the CanvasItemProps class for holding a widget and its properties.
class CanvasItemProps {
Widget canvasItem;
double x = 0, y = 0, dx, dy;
double scale = 1;
Color color;
CanvasItemProps();
CanvasItemProps.fromItem(this.canvasItem);
}
canvasItemProps holds the list of CanvasItemProps
List<CanvasItemProps> canvasItemProps = [];
I have a flatbutton which onPressed execute the following:
var canvasItemObj = CanvasItemProps();
canvasItemProps.add(canvasItemObj);
canvasItemObj.canvasItem = Positioned(
left: canvasItemObj.x,
top: canvasItemObj.y,
child: GestureDetector(
child: Text(
addTextController.text,
textScaleFactor: 5,
),
onVerticalDragUpdate: (dragDetails) {
setState(() {
canvasItemObj.y = dragDetails.localPosition.dy;
canvasItemObj.x = dragDetails.localPosition.dx;
});
},
));
addTextController.text = "";
setState(() {});
},
In build function there is a Stack:
Stack(children:canvasItemProps.isEmpty()?
Text("No Item"):
getCanvasItem)
The following getCanvasItem function returns the list of widget to pass to the Stack widget
List<Widget> getCanvasItem() {
List<Widget> list = [];
for (int i = 0; i < canvasItemProps.length; i++) {
list.add(canvasItemProps[i].canvasItem);
}
return list;
}
On the other hand, this works fine when I am dealing with only one widget that is not dynamically added(x and y are two variables declared in the scope and are initialized to 0.)
double x=0,y=0;
widget is as follows:
Container(
margin: EdgeInsets.fromLTRB(50, 100, 90, 100),
color: Colors.yellow,
child: Stack(
children: <Widget>[
Positioned(
top: y,
left: x,
child: GestureDetector(
onVerticalDragUpdate: (v){
setState(() {
x = v.localPosition.dx;
y = v.localPosition.dy;
});
},
child:Text("Text"),
)
),
],
),
)
The issue is the Top and Left parameters are final so you cant change the values. But instead, you should reinitialize the positioned widget each time with the updated values.