简体   繁体   English

Sliver Appbar [Collapsing Toolbar] 动画标题从左到中 Flutter

[英]Sliver Appbar [Collapsing Toolbar] animate title from left to center in Flutter

Here is my Build method for collapsing toolbar:-这是我用于折叠工具栏的构建方法:-

     @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: CustomScrollView(
        controller: controller,
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            expandedHeight: appBarHeight,
            leading: IconButton(
              icon: Icon(
                Icons.arrow_back_ios,
                color: Colors.black,
              ),
              onPressed: () => null,
            ),
            floating: true,
            flexibleSpace: FlexibleSpaceBar(

          titlePadding: EdgeInsets.only(left:leftV , bottom:bottomV ),
          title: Text(
            "Title ",
            style: TextStyle(
              color: Colors.black,
              fontSize: 16.0,
            ),
          ),
        ),
      ),
      SliverList(delegate:
          SliverChildBuilderDelegate((BuildContext context, int index) {
        return ListTile(title: Text("Flutter / $index"));
      }))
    ],
  ),
);
}

As per the doc I got solution to remove padding:-根据文档,我得到了删除填充的解决方案:-

/// By default the value of this property is /// EdgeInsetsDirectional.only(start: 72, bottom: 16) if the title is /// not centered, EdgeInsetsDirectional.only(start 0, bottom: 16) otherwise. /// 默认情况下,如果标题未居中,则此属性的值为 /// EdgeInsetsDirectional.only(start: 72, bottom: 16) ,否则为EdgeInsetsDirectional.only(start 0, bottom: 16) final EdgeInsetsGeometry titlePadding;最终的 EdgeInsetsGeometry titlePadding;

But I got the output as:-但我得到的 output 是:-

在此处输入图像描述

I want to center the title when the app bar is totally collapsed.我想在应用栏完全折叠时将标题居中。

Issue has been filed in github also check here.问题已在 github 中提交,也请在此处查看。

Found the solution on my own!!!我自己找到了解决方案!!!

Add below code to your Sliver App Bar .........将以下代码添加到您的Sliver 应用程序栏.....................

 flexibleSpace: LayoutBuilder(
                builder:
                    (BuildContext context, BoxConstraints constraints) {
                  double percent =
                      ((constraints.maxHeight - kToolbarHeight) *
                          100 /
                          (appBarHeight - kToolbarHeight));
                  double dx = 0;

                  dx = 100 - percent;
                  if (constraints.maxHeight == 100) {
                    dx = 0;
                  }

                  return Stack(
                    children: <Widget>[
                      Padding(
                        padding: const EdgeInsets.only(
                            top: kToolbarHeight / 4, left: 0.0),
                        child: Transform.translate(
                          child: Text(
                            title,
                            style: MyTextStyle.getAppBarTextStyle(
                                screenUtil, appColors),
                          ),
                          offset: Offset(
                              dx, constraints.maxHeight - kToolbarHeight),
                        ),
                      ),
                    ],
                  );
                },
              ),

Percentage is calculated based on the scroll and it animation has been placed accordingly.百分比是根据滚动计算的,并相应地放置了动画。

在此处输入图片说明

I managed to get a solution with a ScrollController .我设法通过ScrollController获得了解决方案。

Example Result Gif示例结果 Gif

I used this next function:我使用了下一个函数:

double get _horizontalTitlePadding {
    const kBasePadding = 15.0;
    const kMultiplier = 0.5;

    if (_scrollController.hasClients) {
      if (_scrollController.offset < (kExpandedHeight / 2)) {
        // In case 50%-100% of the expanded height is viewed
        return kBasePadding;
      }

      if (_scrollController.offset > (kExpandedHeight - kToolbarHeight)) {
        // In case 0% of the expanded height is viewed
        return (kExpandedHeight / 2 - kToolbarHeight) * kMultiplier +
            kBasePadding;
      }

      // In case 0%-50% of the expanded height is viewed
      return (_scrollController.offset - (kExpandedHeight / 2)) * kMultiplier +
          kBasePadding;
    }

    return kBasePadding;
}

And I used it inside of my SilverAppBar titlePadding :我在SilverAppBar titlePadding使用了它:

  child: Scaffold(
      body: CustomScrollView(
    controller: _scrollController,
    slivers: <Widget>[
      SliverAppBar(
        pinned: true,
        expandedHeight: kExpandedHeight,
        flexibleSpace: FlexibleSpaceBar(
          titlePadding: EdgeInsets.symmetric(
              vertical: 16.0, horizontal: _horizontalTitlePadding),

Just make sure to initialize the controller in initState() :只需确保在initState()初始化控制器:

_scrollController = ScrollController()..addListener(() => setState(() {}));

Edit:编辑:

I ended up creating a better solution that utilizes the transformations already happening within the FlexibleSpaceBar.我最终创建了一个更好的解决方案,它利用了 FlexibleSpaceBar 中已经发生的转换。 After copying the file from the gist into your project, replace FlexibleSpaceBar with MyFlexibleSpaceBar and provide a titlePaddingTween such as将文件从 gist 复制到您的项目中后,将FlexibleSpaceBar替换为MyFlexibleSpaceBar并提供一个titlePaddingTween例如

titlePaddingTween: EdgeInsetsTween(begin: EdgeInsets.only(left: 16.0, bottom: 16), end: EdgeInsets.only(left: 72.0, bottom: 16))

instead of titlePadding.而不是 titlePadding。 The tween will animate from the "begin" EdgeInsets when the appbar is fully expanded to the "end" EdgeInsets when the appbar is collapsed.当应用EdgeInsets完全展开时,补间将从“开始” EdgeInsets动画到当应用EdgeInsets时“结束” EdgeInsets

I also added a foreground parameter that displays above the title and background, but doesn't transform as they do.我还添加了一个显示在标题和背景上方的foreground参数,但不会像它们那样变换。

Original Answer:原答案:

The other answers are good, but they rebuild more widgets than necessary.其他答案很好,但它们重建的小部件比必要的要多。 My solution builds on the other answers but will only rebuild what is within ValueListenableBuilder :我的解决方案建立在其他答案的基础上,但只会重建ValueListenableBuilder

class SamplePage extends StatelessWidget {
  static const _kBasePadding = 16.0;
  static const kExpandedHeight = 250.0;

  final ValueNotifier<double> _titlePaddingNotifier = ValueNotifier(_kBasePadding);

  final _scrollController = ScrollController();

  double get _horizontalTitlePadding {
    const kCollapsedPadding = 60.0;

    if (_scrollController.hasClients) {
      return min(_kBasePadding + kCollapsedPadding,
          _kBasePadding + (kCollapsedPadding * _scrollController.offset)/(kExpandedHeight - kToolbarHeight));
    }

    return _kBasePadding;
  }

  @override
  Widget build(BuildContext context) {
    _scrollController.addListener(() {
      _titlePaddingNotifier.value = _horizontalTitlePadding;
    });

    return Scaffold(

      body: NestedScrollView(
          controller: _scrollController,
          headerSliverBuilder: (context, innerBoxIsScrolled) {
            return <Widget>[
              SliverAppBar(
                  expandedHeight: kExpandedHeight,
                  floating: false,
                  pinned: true,
                  flexibleSpace: FlexibleSpaceBar(
                      collapseMode: CollapseMode.pin,
                      centerTitle: false,
                      titlePadding: EdgeInsets.symmetric(vertical: 16, horizontal: 0),
                      title: ValueListenableBuilder(
                        valueListenable: _titlePaddingNotifier,
                        builder: (context, value, child) {
                          return Padding(
                            padding: EdgeInsets.symmetric(horizontal: value),
                            child: Text(
                              "Title"),
                          );
                        },
                      ),
                      background: Container(color: Colors.green)
                  )
              ),
            ];
          },
          body: Text("Body text")
        ),
    );
  }
}

I had the same issue, I resolved it using LayoutBuilder as the child for the flexibleSpace widget of the SliverAppBar.我遇到了同样的问题,我使用LayoutBuilder作为 SliverAppBar 的flexibleSpace小部件的子项解决了它。 The purpose of the LayoutBuilder is to enable me to know the current position (height) of the appBar. LayoutBuilder的目的是让我知道 appBar 当前的 position(高度)。

I'm using MediaQuery.of(context).size to automatically get the size of the screen.我正在使用MediaQuery.of(context).size来自动获取屏幕的大小。

var top = 0.0;
var appbarThreshold = 140.0;

class _MySliverAppBarState extends State<MySliverAppBar> {
  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;

    return SliverAppBar(
      centerTitle: true,
      pinned: true,
      leading: TextButton(
        child: CircleAvatar(
          radius: size.width / 4,
          backgroundColor: Colors.blue.withOpacity(0.3),
        ),
        onPressed: () {
          print("Hello");
        },
      ),
      leadingWidth: size.width / 4,
      collapsedHeight: size.height / 11.5,
      expandedHeight: size.height / 5,
      backgroundColor: Colors.white,
      foregroundColor: Colors.black,

      flexibleSpace: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          top = constraints.biggest.height;

          return FlexibleSpaceBar(
            title: AnimatedOpacity(
              duration: const Duration(milliseconds: 300),
              opacity: 1.0,
              child: Text(
                top < appbarThreshold ? "Bloom" : "Welcome, Iremide",
                style: TextStyle(
                    fontSize: top < appbarThreshold
                        ? size.height / 30
                        : size.height / 40,
                    color: Colors.black87,
                    fontFamily: 'SourceSansSerif',
                    fontWeight: FontWeight.w700),
              ),
            ),
            titlePadding: top < appbarThreshold
                ? EdgeInsets.fromLTRB(
                    size.width / 4.9, 0.0, 0.0, size.height / 18)
                : EdgeInsets.fromLTRB(
                    size.width / 14, 0.0, 0.0, size.height / 30),
          );
        },
      ),
    );
  }
}

You can adjust the Position of the Title when appBar is collapsed by editing the left padding size here:您可以通过在此处编辑左侧填充大小来调整 appBar折叠时标题的 Position:

//
                titlePadding: top < appbarThreshold
                    ? EdgeInsets.fromLTRB(
                        size.width / 4.9, 0.0, 0.0, size.height / 18)
                    : EdgeInsets.fromLTRB(
                        size.width / 14, 0.0, 0.0, size.height / 30),
              

Regards.问候。

          late ScrollController _scrollController;
          static const kExpandedHeight = 300.0;
        
          @override
          void initState() {
            super.initState();
            _scrollController = ScrollController()..addListener(() => setState(() {}));
          }
        
          double get _horizontalTitlePadding {
            const kBasePadding = 15.0;
            const kMultiplier = 0.5;
        
            if (_scrollController.hasClients) {
              if (_scrollController.offset < (kExpandedHeight / 2)) {
                // In case 50%-100% of the expanded height is viewed
                return kBasePadding;
              }
        
              if (_scrollController.offset > (kExpandedHeight - kToolbarHeight)) {
                // In case 0% of the expanded height is viewed
                return (kExpandedHeight / 2 - kToolbarHeight) * kMultiplier +
                    kBasePadding;
              }
        
              // In case 0%-50% of the expanded height is viewed
              return (_scrollController.offset - (kExpandedHeight / 2)) * kMultiplier +
                  kBasePadding;
            }
        
            return kBasePadding;
          }
    
    
    CustomScrollView(
            controller: _scrollController,
            slivers: [
              SliverAppBar(
                expandedHeight: kExpandedHeight,
                pinned: true,
                flexibleSpace: FlexibleSpaceBar(
                  title: Text(product.title),
                  titlePadding: EdgeInsets.symmetric(
                      vertical: 16.0, horizontal: _horizontalTitlePadding),
                  background: Hero(
                    tag: product.id,
                    child: Image.network(
                      product.imageUrl,
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
              ),
              SliverList(
                  delegate: SliverChildListDelegate([
//add your widgets here
])
        ]
) //end of CustomScrollView

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM