Flutter实战项目-第九篇 吸顶及滚动条嵌套

概要

  • 滚动条嵌套
  • 吸顶
  • CustomScrollView、SliverList

一、滚动条嵌套

现实场景中可能会出现嵌套布局的情况,其实这种场景和吸顶基本是一致,可以采用同样是方法实现上述两种场景。

首先我们可以看下嵌套的效果。

整个页面是一个滚动,白色部分其实是内嵌的滚动。当然我这样看可能和一般的滚动没什么区别。为了更直观的表现嵌套,可以加上吸顶的效果,这样就可以直观的看到嵌套的效果了。

二、完整代码


import 'dart:math';
import 'package:flutter/material.dart';
import './../../component/menu.tab.dart';

class Surprise extends StatefulWidget {
  const Surprise({Key? key}) : super(key: key);
  static const routeName = '/test3';
  @override
  State<Surprise> createState() => _SurpriseState();
}

class _SurpriseState extends State<Surprise> {


  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          toolbarHeight: 0,
          backgroundColor: Colors.white,
          elevation: 0,
        ),
        backgroundColor:const Color(0xFFfafafa),
        body:CustomScrollView(
          slivers: <Widget>[
            _buildBanner(),
            _buildStickyBar(),
            _buildList(),
          ],
        )
      );
  }

  Widget _buildBanner() {
    return SliverToBoxAdapter(
      child:SizedBox(
        height: 200,
        child:Stack(
          children: [
            SizedBox(
              width: double.infinity,
              height: 200,
              child: Image.network(
                "https://img30.360buyimg.com/img/jfs/t1/92581/29/20454/374562/61de544fE1d5e1e34/f69d41d732f3fe81.jpg",
                height: double.infinity,
                fit: BoxFit.fill,
              ),
            ),
            Positioned(
              bottom: -1,
              width: MediaQuery.of(context).size.width,
              child: Container(
                height: 10,
                decoration:const BoxDecoration(
                  color:Color(0xFFfafafa),
                  borderRadius: BorderRadius.only(
                    topLeft: Radius.circular(6),
                    topRight: Radius.circular(6),
                  )
                ),
              )
            )
          ],
        ),
      )
    );
  }

  Widget _buildStickyBar() {
    return SliverPersistentHeader(
      pinned: true, //是否固定在顶部
      floating: true,
      delegate: _SliverAppBarDelegate(
          minHeight: 50, //收起的高度
          maxHeight: 50, //展开的最大高度
          child: Container(
            padding: const EdgeInsets.only(left: 16),
            color:const Color(0xFFfafafa),
            alignment: Alignment.centerLeft,
            child: Row(
              children: [
                Container(
                  width: 50,
                  alignment: Alignment.center,
                  child: const Text("精选", style: TextStyle(fontSize: 18)),
                ),
                Expanded(
                  child:MenuTab(
                    menuList:const [
                      {"value":"1","text":"京喜自营"},
                      {"value":"2","text":"母婴玩具"},
                      {"value":"3","text":"生活百货"},
                      {"value":"4","text":"酒水饮料"},
                      {"value":"5","text":"家清纸品"},
                      {"value":"6","text":"米面粮油"},
                      {"value":"7","text":"数码配件"},
                      {"value":"8","text":"大小家电"},
                      {"value":"9","text":"服饰鞋靴"},
                      {"value":"10","text":"美妆个护"},
                      {"value":"11","text":"休闲零食"},
                    ],
                    onPress: (e){
                      setState(() {
                      });
                    },
                  )
                )
              ],
            ),
          )),
    );
  }

  Widget _buildList() {
    return SliverList(
      delegate: SliverChildBuilderDelegate(
      (context, index) { 
        
        return Container(
          height: 100,
          alignment: Alignment.center,
          child: Text("$index,Container"),
        );
      },
      childCount:10,
    ));
  }
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    required this.minHeight,
    required this.maxHeight,
    required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => max(maxHeight, minHeight);

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}


import 'package:flutter/material.dart';
class MenuTab extends StatefulWidget {
  final Function? onPress;
  final List<Map<String, dynamic>>  menuList;
  const MenuTab({Key? key,required this.menuList,this.onPress}) : super(key: key);

  @override
  State<MenuTab> createState() => _MenuTabState();
}

class _MenuTabState extends State<MenuTab> {
  final ScrollController _controller = ScrollController();
  late String currerValue = "";
  final GlobalKey menuTabListKey = GlobalKey();

  List<Widget> generateMenuTabList (){
    List<Widget> menuTabList = [];
    for(var i = 0; i < widget.menuList.length; i++){
      menuTabList.add(
        InkResponse(
          splashColor:Colors.transparent,
          splashFactory: NoSplash.splashFactory,
          child:Container(
            width: 100,
            alignment: Alignment.center,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  widget.menuList[i]["text"],
                  style: TextStyle(
                    fontWeight: currerValue== widget.menuList[i]["value"] as String?FontWeight.bold:FontWeight.normal,
                    fontSize: currerValue== widget.menuList[i]["value"] as String?20:18,
                    color: currerValue== widget.menuList[i]["value"] as String? const Color(0xFFf81818):const Color(0xFF000000)
                  )
                ),
                SizedBox(
                  width: 50,
                  height: 2,
                  child: DecoratedBox(
                    decoration: BoxDecoration(
                      color: currerValue== widget.menuList[i]["value"] as String?const Color(0xFFf81818):Colors.transparent
                    ),
                  )
                )
              ],
            )
          ),
          onTap: (){
            chosenTab(widget.menuList[i],i);
            if(widget.onPress!=null){
              widget.onPress!(widget.menuList[i]);
            }
          },
        ),
      );
    }
    return menuTabList;
  }


  void chosenTab(item,int index){
    double containerWidth =menuTabListKey.currentContext!.size!.width;
    if(containerWidth/2<((index+1)*100-50)){
      _controller.animateTo((index+1)*100-containerWidth/2,duration: const Duration(seconds: 1), curve: Curves.ease);
    }else{
      _controller.animateTo(0,duration: const Duration(seconds: 1), curve: Curves.ease);
    }
    setState(() {
      currerValue =item['value'] as String;
    });
  }
  @override
  Widget build(BuildContext context) {
    return ListView(
      key: menuTabListKey,
      scrollDirection: Axis.horizontal,
      controller: _controller,
      children: generateMenuTabList(),
    );
  }
}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
很高兴为您解答问题!关于Flutter实战视频-移动电商-48.详细页_详情和评论的切换,我可以提供以下解答: 在Flutter实战视频-移动电商-48中,详细页的详情和评论的切换是通过一个TabBar实现的。具体来说,通过在AppBar中添加一个TabBar来实现不同页面之间的切换。在这个TabBar中,我们可以添加两个Tab,分别为“详情”和“评论”,并分别对应两个页面。当用户点击不同的Tab时,就可以切换到不同的页面。 在Flutter中,TabBar可以使用DefaultTabController和TabBarView来实现。DefaultTabController是一个Widget,它可以管理TabBar和TabBarView之间的关系。TabBarView则是一个Widget,它可以显示不同的页面。 具体实现方法如下: ```dart class DetailPage extends StatefulWidget { @override _DetailPageState createState() => _DetailPageState(); } class _DetailPageState extends State<DetailPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('商品详情'), bottom: TabBar( tabs: <Widget>[ Tab( text: '详情', ), Tab( text: '评论', ), ], ), ), body: DefaultTabController( length: 2, child: TabBarView( children: <Widget>[ // 详情页面 DetailWidget(), // 评论页面 CommentWidget(), ], ), ), ); } } ``` 在上面的代码中,我们首先创建了一个AppBar,并在其中添加了一个TabBar。然后,在Scaffold的body中,我们创建了一个DefaultTabController,并将其length设为2,即有两个页面。最后,我们在TabBarView中添加了两个Widget,即DetailWidget和CommentWidget,分别对应详情页面和评论页面。 希望这个解答能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值