preface

A TAB bar with hidden top appears when the flutter is slid up. Scroll to the corresponding position to switch the TAB bar, similar to the effect of pulling up the baby details page on a shopping website.Copy the code

First, the effect picture:

An overview of the

CustomScrollView is a component provided by Flutter that can be used to customize the scrolling effect. It acts like glue to hold multiple slivers together. By using the suction a top SliverPersistentHeader, SliverAppBar is implemented based on this, SliverPersistentHeader SliverPersistentHeaderDelegate is one of the most important attributeCopy the code

Custom header

SliverPersistentHeaderDelegate implementation class must implement the four methods. Among them:

MinExtent: the height of a component when it is folded up. MaxExtent: the height of the component when expanded. ShouldRebuild: similar to shouldComponentUpdate in React; Build: Builds the rendered content. The code is as follows:

ShrinkOffset > this.thresholdValue Indicates the current header’s scroll offset, and is larger than its incoming threshold to hide and control the display.

class SliverCustomHeaderStatusDelegate extends SliverPersistentHeaderDelegate { BuildContext context; Widget tabs; Widget top; num thresholdValue; num minH; num maxH; SliverCustomHeaderStatusDelegate( {this.context, this.tabs, this.top, this.thresholdValue, this.maxH, this.minH}); @override double get minExtent => this.minH; @override double get maxExtent => this.maxH; @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) { return true; } bool makeStickyTab(shrinkOffset) {// It represents the current header roll offset. } else { return false; } } @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return Container( height: this.maxExtent, width: MediaQuery.of(context).size.width, child: this.makeStickyTab(shrinkOffset) ? tabs : top); }}Copy the code

measurement

Slide down the page to automatically switch the TAB bar according to the drop-down distance. You need to dynamically calculate the top distance of each TAB bar corresponding to the drop-down part, ranging as follows:

Apply for a global key first

var globalKeyOne = GlobalKey();
Copy the code

I can use it for layout

    SliverToBoxAdapter(
      child: Container(
        key: globalKeyOne,
        width: double.infinity,
        height: ScreenUtil().setHeight(400),
        color: Colors.red,
      ),
    ),
Copy the code

Then measure when initState. After a delay, size can be obtained only after state layout is finished

// Wait until the state layout is finished to get size future.delayed (Duration(milliseconds:) 100), () { oneY = getY(globalKeyOne.currentContext); twoY = getY(globalKeyTwo.currentContext); threeY = getY(globalKeyThree.currentContext); fourY = getY(globalKeyFour.currentContext); header = ScreenUtil().setHeight(this.headerMax - this.headerMin) + 10; //10 is error});Copy the code

Get the distance from the top of the widget by:

  double getY(BuildContext buildContext) {
    final RenderBox box = buildContext.findRenderObject();
    //final size = box.size;
    final topLeftPosition = box.localToGlobal(Offset.zero);
    return topLeftPosition.dy;
  }
Copy the code

rolling

Scroll is based on ScrollController’s jumpTo(Double offset), animateTo(Double offset,…) : These two methods are used to jump to a specified location. They differ in that the latter performs an animation while the former does not.

Click the TAB bar for each TAB to switch to the corresponding section, using the measured processing of the distance of each section and sliding the distance to dynamically jumpTo the specified position

TabBar( onTap: (index) { if (! mounted) { return; } switch (index) { case 0: _controller.jumpTo(0); _tabController.animateTo(0); break; case 1: _controller.jumpTo(header); _tabController.animateTo(1); break; case 2: _controller.jumpTo(header + (twoY - oneY)); _tabController.animateTo(2); break; case 3: _controller.jumpTo(header + (threeY - oneY)); _tabController.animateTo(3); break; case 4: _controller.jumpTo(header + (fourY - oneY)); _tabController.animateTo(4); break; }Copy the code

At the same time, listen in initState to scroll to what position, according to the scrolling position to dynamically switch TAB active position. Handles logic in ScrollController listener events.

_controller.addListener(() { var of = _controller.offset; Var distance_2 = header + (twoy-oney); Var distance_3 = header + (threey-oney); Var distance_4 = header + (foury-oney); var distance_4 = header + (foury-oney); if (of > header && of < distance_2) { _tabController.animateTo(1); } else if (of > distance_2 && of < distance_3) { _tabController.animateTo(2); } else if (of > distance_3 && of < distance_4) { _tabController.animateTo(3); } else if (of > distance_4) { _tabController.animateTo(4); }});Copy the code

NestedScrollView, SingleChildScrollView, SingleChildScrollView, SingleChildScrollView, SingleChildScrollView, SingleChildScrollView, SingleChildScrollView, SingleChildScrollView I still don’t understand sliver very well, but I changed it to CustomScrollView, and I put _controller on CustomScrollView,

    return SafeArea(
      child: Scaffold(
        backgroundColor: Color(0xFF201E24),
        body: CustomScrollView(
          controller: _controller,
          slivers: [
            SliverPersistentHeader(
              pinned: true,
              delegate: SliverCustomHeaderStatusDelegate(
                tabs: _tab,
                top: _top,
                minH: ScreenUtil().setHeight(this.headerMin),
                maxH: ScreenUtil().setHeight(this.headerMax),
                thresholdValue:
                ScreenUtil().setHeight(this.headerMax - this.headerMin),
                context: context,
              ),
            ),
            SliverToBoxAdapter(
              child: Container(
                key: globalKeyOne,
                width: double.infinity,
                height: ScreenUtil().setHeight(400),
                color: Colors.red,
              ),
            ),
            SliverToBoxAdapter(
              child: Container(
                key: globalKeyTwo,
                width: double.infinity,
                height: ScreenUtil().setHeight(1400),
                color: Colors.green,
              ),
            ),
            SliverToBoxAdapter(
              child: Container(
                key: globalKeyThree,
                width: double.infinity,
                height: ScreenUtil().setHeight(1800),
                color: Colors.grey,
              ),
            ),
            SliverToBoxAdapter(
              child: Container(
                key: globalKeyFour,
                width: double.infinity,
                height: ScreenUtil().setHeight(400),
                color: Colors.pinkAccent,
              ),
            ),
          ],
        ),
      ),
    );
Copy the code

Specific full code in the project address


Welcome to learn and share FLUTTER with me. The project will continue to update new learning demo

Github address of this project: project address

Here is our public account: Flutter Programming Notes (CODE9871)Copy the code

The official accounts share their learning ideas from time to time

Past review:

The Flutter implements a digital scale

Flutter implements simple rotation animations

The Flutter achieves a simple drawer effect

The flutter achieves top adsorption effect