tp7309 / flutter_sticky_and_expandable_list

粘性头部与分组列表Sliver实现 Build a grouped list, which support expand/collapse section and sticky headers, support use it with sliver widget.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sticky header issue in Tab layout

Shamik07 opened this issue · comments

I am trying to implement the ExampleCustomSectionAnimation in a TabLayout

image

But as soon as I scroll and the AppBar collapses the header no longer remains sticky and gets under the AppBar. This AppBar is in the TabLayout Main view

image

But if give another AppBar in the ExampleCustomSectionAnimation list view - In Red over here

image

The header becomes sticky again

image

I want the headers to remain sticky without the additional AppBar (Red),

Thanks for the package, Love it!

The issue can be easily replicated by placing the ExampleCustomSectionAnimation provided in the sample in a TabBarView

MainTabLayout
image

import 'package:flutter/material.dart';
import 'package:sticky_and_expandable_list/sticky_and_expandable_list.dart';

import 'mock_data.dart';

class ExampleTabLayoutCustomSectionAnimation extends StatefulWidget {
  @override
  _ExampleTabLayoutCustomSectionAnimationState createState() =>
      _ExampleTabLayoutCustomSectionAnimationState();
}

class _ExampleTabLayoutCustomSectionAnimationState
    extends State<ExampleTabLayoutCustomSectionAnimation> {
  var sectionList = MockData.getExampleSections(10, 10);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: PreferredSize(
          preferredSize: const Size.fromHeight(50),
          child: Container(color: Colors.red),
        ),
        body: ExpandableListView(
          builder: SliverExpandableChildDelegate<String, ExampleSection>(
            sectionList: sectionList,
            itemBuilder: (context, sectionIndex, itemIndex, index) {
              String item = sectionList[sectionIndex].items[itemIndex];
              return ListTile(
                leading: CircleAvatar(
                  child: Text("$index"),
                ),
                title: Text(item),
              );
            },
            sectionBuilder: (context, containerInfo) => _SectionWidget(
              section: sectionList[containerInfo.sectionIndex],
              containerInfo: containerInfo,
              onStateChanged: () {
                //notify ExpandableListView that expand state has changed.
                WidgetsBinding.instance.addPostFrameCallback((_) {
                  if (mounted) {
                    setState(() {});
                  }
                });
              },
            ),
          ),
        ));
  }
}

class _SectionWidget extends StatefulWidget {
  final ExampleSection section;
  final ExpandableSectionContainerInfo containerInfo;
  final VoidCallback onStateChanged;

  _SectionWidget(
      {@required this.section,
      @required this.containerInfo,
      @required this.onStateChanged})
      : assert(onStateChanged != null);

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

class __SectionWidgetState extends State<_SectionWidget>
    with SingleTickerProviderStateMixin {
  static final Animatable<double> _halfTween =
      Tween<double>(begin: 0.0, end: 0.5);
  AnimationController _controller;

  Animation _iconTurns;

  Animation<double> _heightFactor;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    _iconTurns =
        _controller.drive(_halfTween.chain(CurveTween(curve: Curves.easeIn)));
    _heightFactor = _controller.drive(CurveTween(curve: Curves.easeIn));

    if (widget.section.isSectionExpanded()) {
      _controller.value = 1;
    }
  }

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

  @override
  Widget build(BuildContext context) {
    widget.containerInfo
      ..header = _buildHeader(context)
      ..content = _buildContent(context);
    return ExpandableSectionContainer(
      info: widget.containerInfo,
    );
  }

  Widget _buildHeader(BuildContext context) {
    return Container(
      color: Colors.lightBlue,
      child: ListTile(
        title: Text(
          widget.section.header,
          style: TextStyle(color: Colors.white),
        ),
        trailing: RotationTransition(
          turns: _iconTurns,
          child: const Icon(
            Icons.expand_more,
            color: Colors.white70,
          ),
        ),
        onTap: _onTap,
      ),
    );
  }

  void _onTap() {
    widget.section.setSectionExpanded(!widget.section.isSectionExpanded());
    if (widget.section.isSectionExpanded()) {
      widget?.onStateChanged();
      _controller.forward();
    } else {
      _controller.reverse().then((_) {
        widget?.onStateChanged();
      });
    }
  }

  Widget _buildContent(BuildContext context) {
    return SizeTransition(
      sizeFactor: _heightFactor,
      child: SliverExpandableChildDelegate.buildDefaultContent(
          context, widget.containerInfo),
    );
  }
}
import 'dart:io';

import 'package:example/example_custom_section_animation.dart';
import 'package:example/example_tab_layout_custom_section_animation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class TabLayoutView extends StatelessWidget {
  const TabLayoutView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ScrollController scrollController;
    TabController tabController;
    return DefaultTabController(
      length: 5,
      child: Scaffold(
        body: new NestedScrollView(
          controller: scrollController,
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              new SliverAppBar(
                pinned: true,
                floating: true,
                //forceElevated: true,
                //forceElevated: innerBoxIsScrolled, for complete flat
                forceElevated: !innerBoxIsScrolled,
                elevation: 2,
                backgroundColor: Colors.indigo,
                title: _buildTitle(context),
                bottom: new TabBar(
                  indicatorColor: Colors.amberAccent,
                  indicatorSize: TabBarIndicatorSize.label,
                  indicatorWeight: 5,
                  isScrollable: true,
                  tabs: <Tab>[
                    Tab(icon: Icon(Icons.music_note)),
                    Tab(icon: Icon(Icons.music_video)),
                    Tab(icon: Icon(Icons.camera_alt)),
                    Tab(icon: Icon(Icons.grade)),
                    Tab(icon: Icon(Icons.email)),
                  ],
                  controller: tabController,
                ),
              ),
            ];
          },
          body: new TabBarView(
            children: <Widget>[
              ExampleTabLayoutCustomSectionAnimation(),
              ExampleCustomSectionAnimation(),
              ExampleTabLayoutCustomSectionAnimation(),
              ExampleCustomSectionAnimation(),
              ExampleTabLayoutCustomSectionAnimation(),
            ],
            controller: tabController,
          ),
        ),
      ),
    );
  }
}

Widget _buildTitle(BuildContext context) {
  /*var horizontalTitleAlignment =
  Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;*/
  var horizontalTitleAlignment = CrossAxisAlignment.center;
  if(!kIsWeb){
    horizontalTitleAlignment = Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;
  }
  return new InkWell(
    //onTap: () => scaffoldKey.currentState.openDrawer(),
    child: new Padding(
      padding: const EdgeInsets.symmetric(horizontal: 12.0),
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: horizontalTitleAlignment,
        children: <Widget>[
          const Text('Tab Layout'),
        ],
      ),
    ),
  );
}

Hi! Apologies for creating this issue. There was nothing wrong with your package, It was just a flutter bug or wrong implementation of NestedScrollView on my part.

image

Here is the bug -
flutter/flutter#22393

Here is the fix -

import 'dart:io';

import 'package:example/example_custom_section_animation.dart';
import 'package:example/example_tab_layout_custom_section_animation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class TabLayoutView extends StatelessWidget {
  const TabLayoutView({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ScrollController scrollController;
    TabController tabController;
    return DefaultTabController(
      length: 5,
      child: Scaffold(
        body: new NestedScrollView(
          controller: scrollController,
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              SliverOverlapAbsorber(
                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: SliverSafeArea(
                  top: false,
                  sliver: new SliverAppBar(
                    pinned: true,
                    floating: true,
                    //forceElevated: true,
                    //forceElevated: innerBoxIsScrolled, for complete flat
                    forceElevated: !innerBoxIsScrolled,
                    elevation: 2,
                    backgroundColor: Colors.indigo,
                    title: _buildTitle(context),
                    bottom: new TabBar(
                      indicatorColor: Colors.amberAccent,
                      indicatorSize: TabBarIndicatorSize.label,
                      indicatorWeight: 5,
                      isScrollable: true,
                      tabs: <Tab>[
                        Tab(icon: Icon(Icons.music_note)),
                        Tab(icon: Icon(Icons.music_video)),
                        Tab(icon: Icon(Icons.camera_alt)),
                        Tab(icon: Icon(Icons.grade)),
                        Tab(icon: Icon(Icons.email)),
                      ],
                      controller: tabController,
                    ),
                  ),
                ),
              ),
            ];
          },
          body: new TabBarView(
            children: <Widget>[
              ExampleTabLayoutCustomSectionAnimation(),
              ExampleCustomSectionAnimation(),
              ExampleTabLayoutCustomSectionAnimation(),
              ExampleCustomSectionAnimation(),
              ExampleTabLayoutCustomSectionAnimation(),
            ],
            controller: tabController,
          ),
        ),
      ),
    );
  }
}

Widget _buildTitle(BuildContext context) {
  /*var horizontalTitleAlignment =
  Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;*/
  var horizontalTitleAlignment = CrossAxisAlignment.center;
  if(!kIsWeb){
    horizontalTitleAlignment = Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;
  }
  return new InkWell(
    //onTap: () => scaffoldKey.currentState.openDrawer(),
    child: new Padding(
      padding: const EdgeInsets.symmetric(horizontal: 12.0),
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: horizontalTitleAlignment,
        children: <Widget>[
          const Text('Tab Layout'),
        ],
      ),
    ),
  );
}

Sorry!

Thank you for you feedback ^_^