How to get the index of the main item on screen from 2 nested PageViews?

Issue

I have nested PageViews, the outer one is vertical (can scroll up and down) and the inner pageview is horisontal (can go left and right) – In each row of the vertical scrol view I have horizontal pageview.

Based on the code here, I get the correct index for outer pageview which I call it rowIndx but wrong one for inner pageview. For example when I am on rowIndx = 0 and columnIndx = 2 I see correct numbers printed in the console, when I scroll down, I get rowIndx = 1 and columnIndx = 2 instead of rowIndx = 1 and columnIndx = 0.

The index of columns will update only if you scroll right or left at least once in each row.
How can I get correct index immediately on any card?

You can get index printed in console by tapping on each card. The text on each card shows the correct number for columnIndx to make it easy to compare with what is printed in the console.

Thanks for your help in advance.

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatefulWidget {

  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  int rowIndx = 0;
  int columnIndx = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
          onPageChanged: (index) {
            rowIndx = index;
          },
          scrollDirection: Axis.vertical,
          controller: PageController(initialPage: 0, viewportFraction: 0.63),
          itemCount: 5,
          itemBuilder: (_, index) {
            return PageView.builder(
                onPageChanged: (index) {
                  columnIndx = index;
                },
                controller:
                    PageController(initialPage: 0, viewportFraction: 0.63),
                itemCount: sampleCard.length,
                itemBuilder: (context, ind) {
                  return GestureDetector(
                    onTap: () {
                      print(
                          'column : $columnIndx   in horizontal scroll > left and right');
                      print(
                          'row : $rowIndx   in vertical scroll > up and down');
                    },
                    child: sampleCard[ind],
                  );
                });
          }),
    );
  }
}
List sampleCard = [
  Container(
    margin: EdgeInsets.all(50.0),
    decoration: BoxDecoration(
      color: Colors.red,
    ),
    child: Center(
        child: Text(
      'column 0',
    )),
  ),
  Container(
    margin: EdgeInsets.all(50.0),
    decoration: BoxDecoration(
      color: Colors.blueGrey,
    ),
    child: Center(
        child: Text(
      'column 1',
    )),
  ),
  Container(
    margin: EdgeInsets.all(50.0),
    decoration: BoxDecoration(
      color: Colors.yellow,
    ),
    child: Center(
        child: Text(
      'column 2',
    )),
  ),
];```

Solution

I found that I can seperate the return widget in inner pageview into a stateless widget and get the column and row from that widget as following:

class MyHomePage extends StatefulWidget {

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int rowIndx = 0;
  int columnIndx = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
          onPageChanged: (index) {
            rowIndx = index;
          },
          scrollDirection: Axis.vertical,
          controller: PageController(initialPage: 0, viewportFraction: 0.63),
          itemCount: 5,
          itemBuilder: (_, index) {
            return PageView.builder(
                onPageChanged: (index) {
                  columnIndx = index;
                },
                controller:
                    PageController(initialPage: 0, viewportFraction: 0.63),
                itemCount: sampleCard.length,
                itemBuilder: (context, ind) {
                  return _Cards(
                    column: ind,
                    row: index,
                  );
                });
          }),
    );
  }
}

class _Cards extends StatelessWidget {
  final int column;
  final int row;

  const _Cards({Key? key, required this.column, required this.row})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        print('column : $column   in horizontal scroll > left and right');
        print('row : $row   in vertical scroll > up and down');
      },
      child: sampleCard[column],
    );
  }
}

Answered By – Zahra

Answer Checked By – Pedro (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *