'Carousel image on different page with onPressed index

I am kinda new to flutter. I need help on Carousel images. I have carousel images using carousel_pro package. And i can open image on new page when pressed using GestureDetector. But i would like to open same carousel images on new page but with onPressed index at first. i mean for ex. i have 5 images. When i press on 3rd image it has to open on new carousel with that image not first one. I hope i made myself clear. Below is one-page-for-one-image kinda way. i need carousel with starting index of pressed image in carousel of images. Thanks in advance.

import 'package:carousel_slider/carousel_slider.dart';
import './image_screen.dart';

void main() => runApp(MaterialApp(home: Demo()));

class Demo extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<Demo> {
  @override
  Widget build(BuildContext context) {
    Widget image_carousel = new Container(
        height: 345.0,
        child: CarouselSlider(
          height: 400.0,
          items: [
            'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
            'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg',
            'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
            'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg'
          ].map((i) {
            return Builder(
              builder: (BuildContext context) {
                return Container(
                    width: MediaQuery.of(context).size.width,
                    margin: EdgeInsets.symmetric(horizontal: 5.0),
                    decoration: BoxDecoration(color: Colors.amber),
                    child: GestureDetector(
                        child: Image.network(i, fit: BoxFit.fill),
                        onTap: () {
                          Navigator.push<Widget>(
                            context,
                            MaterialPageRoute(
                              builder: (context) => ImageScreen(i),
                            ),
                          );
                        }));
              },
            );
          }).toList(),
        ));

    return Scaffold(
      body: new Column(
        children: <Widget>[
          image_carousel,
        ],
      ),
    );
  }
}

EDIT: I have added above code for sample. It is not what i need. I need smth like this: Page 1 has carousel with 5 images. When i press 3rd image, on Page2 same carousel of images opens with index of that pressed 3rd image. i mean on Page 2 carousel has to start with 3rd image. I hope i made myself clear this time.



Solution 1:[1]

ANSWER VERSION 2.0

See, the UI element works correct, what needs to be done, is to find a way out to set the item at first and then push all the elements after that

Here, we need to understand what DART SUBLIST is. Please read about it carefully to understand the concepts.

ALGORITHM

1. Select index of the image urls
2. If the item which is selected is first, then normal list would be passed
3. If not, then we get firstElements which is a sublist of (0, index), index not included [for example => If we select 3 from 1,2,3,4 => firstElements = [1,2]]
4. We maintain the lastElements, which is a sublist of (index, list.length), where list.length is ignore. [for example => If we select 3 from 1,2,3,4 => lastElements= [3,4]]
4. Finally we make a final list by adding lastElements + firstElements = [3,4,1,2]

And this is what we're trying to achieve, don't we?

CODE IMPLEMENTATION

  // suppose we have an array of Ints
  List<int> data = [1,2,3,4];
  
  // to keep a pointer, which one is selected
  // here we selected 3, so index will have 2 as it's value, 
  // which is the index of 3
  int index = data.indexOf(3);
  
  // now let say, we want to push 3 to the top, keeping 
  // 4, after it, and 1,2 after 4 in the same format 
  // similar to what you want to do with the selected image
  // CHECKS FOR FIRST ELEMENT IS SELECTED OR NOT, WHICH HAS INDEX 0
  if(index != 0){
    // get the sublist from 0 to index-1
    var firstElements = data.sublist(0, index);
    // get the sublist from index, to last
    var restElements = data.sublist(index, data.length);
    
    // restElements should be pushed to top, since we have want the 
    // to be there, then comes firstElements
    print(restElements+firstElements);
  }else{
    // since the first item is selected
    print(data);
  } 
  
  print(items);

  // OUTPUT => [3, 4, 1, 2]

See, how the above item 3, pushed to the first position with 4 after it, and rest is pushed to it's other position respectively. This is what we're trying to achieve. You can go ahead, and test out for some other values too

REMEMBER: Logic should be clear, and rest UI can take care of itself

I have not used any CarouselSlider, just used my own ListView to show, how it is done. If you get the idea, I am sure, you get how to work on it as well.

HomePage

class _MyHomePageState extends State<MyHomePage> {
  // In the list, I have used all the images as different to show you the difference
  // in a clear way
  List<String> _imageUrls = [
    'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
    'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg',
    'https://static.toiimg.com/thumb/msid-54559212,width-748,height-499,resizemode=4,imgsize-307081/Bangalore.jpg',
    'https://images.unsplash.com/photo-1535332371349-a5d229f49cb5?ixlib=rb-1.2.1&w=1000&q=80'
  ];
  
  // This is the widget which is responsible for creating 
  // list of images as a slider
  Widget get myWidget{
    List<Widget> _widgets = [SizedBox(width: 20.0)];
    
    for(int i=0; i<_imageUrls.length; i++){
      _widgets.add(
        GestureDetector(
          onTap: (){
            Navigator.of(context).push(
              // Our new page, takes ImageUrls list, and the selected index
             // top perform an operation
              MaterialPageRoute(
                builder: (context) => NewPage(imgUrls: _imageUrls, index: i)
              )
            );
          },
          child: Container(
            width: 300.0,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(15.0),
              image: DecorationImage(
                fit: BoxFit.cover,
                image: NetworkImage(_imageUrls[i])
              )
            )
          )
        )
      );
      
      _widgets.add(SizedBox(width: 20.0));
    }
    
    return Container(
      height: 400.0,
      child: ListView(
        shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        children: _widgets
      )
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        height: double.infinity,
        width: double.infinity,
        child: Center(
          child: this.myWidget
        )
      )
    );
  }
}

NewPage

class _NewPageState extends State<NewPage> {
  // This will keep a copy of all the items coming from immutable imageUrls
  // We will be doing operation in this list only
  // it has to be initialized as an empty array
  List<String> _newImageUrls = [];
  
  @override
  void initState(){
    super.initState();
    
    // same algo which is explained above
    if(widget.index != 0){
      // get the sublist from 0 to index-1
      var firstElements = widget.imgUrls.sublist(0, widget.index);
      // get the sublist from index, to last
      var restElements = widget.imgUrls.sublist(widget.index, widget.imgUrls.length);

      setState(() => _newImageUrls = restElements + firstElements);
    }else{
      // since the first item is selected
      // no _newImageUrls = widget.imgUrls cos, machine will
      // understand that both the items are same, so if one changes,
      // that means another has to change. So strict no-no to that
      widget.imgUrls.forEach((item){
        _newImageUrls.add(item);
      });
    }
  }
  
  // now this is same as our HomePage 
  // just a height change of the images and we're using our new 
  // list, not the passed one, since, it has changed data now
  Widget get myWidget{
    List<Widget> _widgets = [SizedBox(width: 20.0)];
    
    for(int i=0; i<_newImageUrls.length; i++){
      _widgets.add(
        Container(
          width: 300.0,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(15.0),
            image: DecorationImage(
              fit: BoxFit.cover,
              image: NetworkImage(_newImageUrls[i])
            )
          )
        )
      );
      
      _widgets.add(SizedBox(width: 20.0));
    }
    
    return Container(
      height: 500.0,
      child: ListView(
        shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        children: _widgets
      )
    );
  }
  
  @override
  Widget build(BuildContext context) {
    
    return Scaffold(
      appBar: AppBar(
        title: Text('New Page'),
      ),
      body: Container(
        height: double.infinity,
        width: double.infinity,
        child: Center(
          child: this.myWidget
        )
      )
    );
  }
}

The result you will get is pretty much the one which you want. That is, If the 3rd item is selected from 1,2,3,4 the other NewPage will show 3,4,1,2 and so on..

RESULT OF OUR NEW WORK WITH CORRECT REQUIREMENTS

Can you see the difference, in the code for NewPage? It is just the logic, else, the UI was as same as before. This is what I was trying to tell you.

Solution 2:[2]

Here is a code sample that might help you out.
main Screen

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("test"),
      ),
      body: Container(
        height: 345.0,
        child: CarouselSlider(
          options: CarouselOptions(
            height: 400,
          ),
          items: [
            'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
            'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg',
            'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
            'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg'
          ].map(
            (i) {
              return Container(
                width: MediaQuery.of(context).size.width,
                margin: EdgeInsets.symmetric(horizontal: 5.0),
                decoration: BoxDecoration(color: Colors.amber),
                child: GestureDetector(
                  child: Image.network(i, fit: BoxFit.fill),
                  onTap: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (context) => Screen2(
                          image: i,
                        ),
                      ),
                    );
                  },
                ),
              );
            },
          ).toList(),
        ),
      ),
    );
  }
}

Screen2: a screen with a carousel and appBar to back into first screen

class Screen2 extends StatelessWidget {
  final List<String> images;
  final String selectedImages;
  Screen2({this.images, this.selectedImages});
  @override
  Widget build(BuildContext context) {
    images.removeWhere((i) => i == selectedImages);
    images.insert(0, selectedImages);
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        height: double.maxFinite,
        width: double.maxFinite,
        child: CarouselSlider(
          options: CarouselOptions(
            viewportFraction: 1.0,
            height: double.maxFinite,
          ),
          items: [
            ...images.map(
              (image) => Image.network(
                image,
                fit: BoxFit.fill,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

result video : click me click me

Solution 3:[3]

You should pass desired index along with your images list and set it to initialPage property of CarouselOptions. https://pub.dev/packages/carousel_slider#params

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:test_eclipse_digital/model/album/photo.dart';

class PhotoCarouselPage extends StatelessWidget {
  final List<Photo> photos;
  final int startFrom;

  const PhotoCarouselPage({
    Key? key,
    required this.photos,
    required this.startFrom
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Builder(
        builder: (context) {
          final double height = MediaQuery.of(context).size.height;
          return CarouselSlider(
            options: CarouselOptions(
              height: height,
              viewportFraction: 1,
              enlargeCenterPage: false,
              initialPage: startFrom,
            ),
            items: photos.map((photo) => Center(
              child: Image.network(
                photo.url,
                fit: BoxFit.cover,
                height: height,),
            )).toList(),
          );
        },
      ),
    );
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 halfer
Solution 2
Solution 3 tanyapog