Phill Wiggins
Phill Wiggins

Reputation: 2927

Flutter Error: 'indexOf(child) > index': is not true. (StreamBuilder,PageView)

I'm trying to create a screen that is contained within a pageview, that also contains a page view for part of the screen.

To acheive this I have an unlimited page view for the whole page itself, then every page has a header view, with a bottom half that has a page view with 3 possible options. I have this pretty much working, however, the pages I am using I would like a StreamBuilder... This is where the issue is caused.

class DiaryPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _DiaryPage();
}

class _DiaryPage extends State<DiaryPage> with TickerProviderStateMixin {
  DiaryBloc _diaryBloc;
  TabController _tabController;
  PageController _pageController;

  @override
  void initState() {
    _diaryBloc = BlocProvider.of<DiaryBloc>(context);
    _diaryBloc.init();

    _tabController = TabController(length: 3, vsync: this);
    _pageController = PageController(initialPage: _diaryBloc.initialPage);

    super.initState();
  }

  @override
  void dispose() {
    _diaryBloc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

    return Flexible(
      child: PageView.builder(
        controller: _pageController,
        itemBuilder: (BuildContext context, int position) {
          return _buildPage(_diaryBloc.getDateFromPosition(position));
        },
        itemCount: _diaryBloc.amountOfPages,
      ),
    );
  }

  Widget _buildPage(DateTime date) {
    return Column(
      mainAxisSize: MainAxisSize.max,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[_getHeader(date), _getTabBody()],
    );
  }

  Widget _getHeader(DateTime date) {
    return Card(
      child: SizedBox(
        width: double.infinity,
        height: 125,
        child: Column(
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.fromLTRB(8, 16, 8, 0),
              child: Text(
                '${DateFormat('EEEE').format(date)} ${date.day} ${DateFormat('MMMM').format(date)}',
                style: Theme.of(context).textTheme.subtitle,
                textScaleFactor: 1,
                textAlign: TextAlign.center,
              ),
            ),
            Row(
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                IconButton(
                  icon: const Icon(Icons.chevron_left),
                  onPressed: () => {
                        _pageController.previousPage(
                            duration: Duration(milliseconds: 250),
                            curve: Curves.ease)
                      },
                ),
                const Expanded(child: LinearProgressIndicator()),
                IconButton(
                  icon: const Icon(Icons.chevron_right),
                  onPressed: () => {
                        _pageController.nextPage(
                            duration: Duration(milliseconds: 250),
                            curve: Curves.ease)
                      },
                ),
              ],
            ),
            Container(
              height: 40.0,
              child: DefaultTabController(
                length: 3,
                child: Scaffold(
                  backgroundColor: Colors.white,
                  appBar: TabBar(
                    controller: _tabController,
                    unselectedLabelColor: Colors.grey[500],
                    labelColor: Theme.of(context).primaryColor,
                    tabs: const <Widget>[
                      Tab(icon: Icon(Icons.pie_chart)),
                      Tab(icon: Icon(Icons.fastfood)),
                      Tab(icon: Icon(Icons.directions_run)),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _getTabBody() {
    return Expanded(
      child: TabBarView(
        controller: _tabController,
        children: <Widget>[
          _getOverviewScreen(),
          _getFoodScreen(),
          _getExerciseScreen(),
        ],
      ),
    );
  }

  // TODO - this seems to be the issue, wtf and why
  Widget _getBody() {
    return Flexible(
      child: StreamBuilder<Widget>(
        stream: _diaryBloc.widgetStream,
        initialData: _diaryBloc.buildEmptyWidget(),
        builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
          return snapshot.data;
        },
      ),
    );
  }

  Widget _getExerciseScreen() {
    return Text("Exercise Screen"); //_getBody();
  }

  Widget _getFoodScreen() {
    return Text("Food Screen"); //_getBody();
  }

  Widget _getOverviewScreen() {
    return _getBody();
  }
}

As you can see, there are three widgets being returned as part of the sub page view, 2 of them are Text Widgets which show correctly, but the StreamBuilder, which is populated correctly with another Text Widget seems to give me the red screen of death. Any ideas?

Upvotes: 4

Views: 9607

Answers (3)

Suat &#214;zkaya
Suat &#214;zkaya

Reputation: 775

In my case, I had a segmentedcontrol/pageView and the children were the same widget type. So, I had to use key for both of them.

    final content = _selectedSegment == TaskCardType.taskCard
    ? TaskCardListView(
        nonRoutine: false,
        key: const Key("A"),
      )
    : TaskCardListView(
        nonRoutine: true,
        key: const Key("B"),
      );

Upvotes: 0

Paul
Paul

Reputation: 1865

For custom ListView/PageView

In my case, I wanted to clear the list of my listview. In a custom ListView/PageView, the findChildIndexCallback will find the element's index after i.e. a reordering operation, but also when you clear the list.

yourList.indexWhere()unfortunately returns -1 when it couldn't find an element. So, Make sure to return null in that case, to tell the callback that the child doesn't exist anymore.

...

findChildIndexCallback: (Key key) {
   final ValueKey<String> valueKey = key as ValueKey<String>;
   final data = valueKey.value;
   final index = images.indexWhere((element) => element.id == data);

   //important here:
   if (index > 0 ) return index;
   else return null;
},


Upvotes: 2

Phill Wiggins
Phill Wiggins

Reputation: 2927

Fixed the problem, it was related to the StreamBuilder being wrapped in a Flexible rather than a column. I then added column to have a mainAxisSize of max... Seemed to work.

Upvotes: 2

Related Questions