簡體   English   中英

Sliver Appbar [Collapsing Toolbar] 動畫標題從左到中 Flutter

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

這是我用於折疊工具欄的構建方法:-

     @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"));
      }))
    ],
  ),
);
}

根據文檔,我得到了刪除填充的解決方案:-

/// 默認情況下,如果標題未居中,則此屬性的值為 /// EdgeInsetsDirectional.only(start: 72, bottom: 16) ,否則為EdgeInsetsDirectional.only(start 0, bottom: 16) 最終的 EdgeInsetsGeometry titlePadding;

但我得到的 output 是:-

在此處輸入圖像描述

我想在應用欄完全折疊時將標題居中。

問題已在 github 中提交,也請在此處查看。

我自己找到了解決方案!!!

將以下代碼添加到您的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),
                        ),
                      ),
                    ],
                  );
                },
              ),

百分比是根據滾動計算的,並相應地放置了動畫。

在此處輸入圖片說明

我設法通過ScrollController獲得了解決方案。

示例結果 Gif

我使用了下一個函數:

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;
}

我在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),

只需確保在initState()初始化控制器:

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

編輯:

我最終創建了一個更好的解決方案,它利用了 FlexibleSpaceBar 中已經發生的轉換。 將文件從 gist 復制到您的項目中后,將FlexibleSpaceBar替換為MyFlexibleSpaceBar並提供一個titlePaddingTween例如

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

而不是 titlePadding。 當應用EdgeInsets完全展開時,補間將從“開始” EdgeInsets動畫到當應用EdgeInsets時“結束” EdgeInsets

我還添加了一個顯示在標題和背景上方的foreground參數,但不會像它們那樣變換。

原答案:

其他答案很好,但它們重建的小部件比必要的要多。 我的解決方案建立在其他答案的基礎上,但只會重建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")
        ),
    );
  }
}

我遇到了同樣的問題,我使用LayoutBuilder作為 SliverAppBar 的flexibleSpace小部件的子項解決了它。 LayoutBuilder的目的是讓我知道 appBar 當前的 position(高度)。

我正在使用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),
          );
        },
      ),
    );
  }
}

您可以通過在此處編輯左側填充大小來調整 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),
              

問候。

          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