溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Flutter?Flow如何實現(xiàn)滑動顯隱層

發(fā)布時間:2023-03-09 14:56:36 來源:億速云 閱讀:144 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“Flutter Flow如何實現(xiàn)滑動顯隱層”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Flutter Flow如何實現(xiàn)滑動顯隱層”吧!

實現(xiàn)思路

思路其實非常簡單,監(jiān)聽橫向滑動的手勢事件,根據(jù)偏移量讓上層組件進(jìn)行偏移。當(dāng)放手時,根據(jù)偏移量是否達(dá)到寬度的一半,使用動畫進(jìn)行移出或者關(guān)閉。

Flutter?Flow如何實現(xiàn)滑動顯隱層

偏移的實現(xiàn)方式有很多,但需要自由地進(jìn)行布局和矩陣變換、透明度,并且需要支持動畫的變化,F(xiàn)low 組件是一個非常不錯的選擇。 Flow 組件可以通過代理類對子組件進(jìn)行自定義布局,靈活性極強(qiáng);如果是 CustomPaint繪制之王 可以繪制萬物,那么 Flow 就是 布局之王,可以擺放萬物。

另外,在滑動過程中需要注意限制偏移量,使偏移量在 0~size.width 之內(nèi);當(dāng)放手時,通過動畫控制器來驅(qū)動動畫,使用補(bǔ)間讓偏移量運動到 0 (打開) 或 size.width(關(guān)閉) 。當(dāng)關(guān)閉時,在右下角展示一個按鈕用于點擊展開:

Flutter?Flow如何實現(xiàn)滑動顯隱層

布局的代碼實現(xiàn)

Flow 組件布局最重要的是實現(xiàn) FlowDelegate,在其中的 paintChildren 方法中實現(xiàn)布局的邏輯。和 CustomPainter 類似,FlowDelegate 的實現(xiàn)類也可以通過 super 構(gòu)造為 repaint 入?yún)⒃O(shè)置可監(jiān)聽對象??杀O(jiān)聽對象的變化會觸發(fā) paintChildren 重新繪制:

SwipeFlowDelegate 實現(xiàn)類再構(gòu)造時傳入可監(jiān)聽對象 offsetX,在繪制索引為 1 的孩子時,通過 Matrix4 進(jìn)行偏移。這樣只要在手勢水平滑動中,更新 offsetX 值即可。另外,可以根據(jù) offsetX.value 是否達(dá)到 size.width 知道是否是關(guān)閉狀態(tài),如果已經(jīng)關(guān)閉,繪制按鈕。

class SwipeFlowDelegate extends FlowDelegate {
  final ValueListenable<double> offsetX;
  SwipeFlowDelegate(this.offsetX) : super(repaint: offsetX);
  @override
  void paintChildren(FlowPaintingContext context) {
    Size size = context.size;
    context.paintChild(0);
    Matrix4 offsetM4 = Matrix4.translationValues(offsetX.value, 0, 0);
    context.paintChild(1, transform: offsetM4);
    // 偏移量對于父級尺寸
    if (offsetX.value == size.width) {
      Matrix4 m1 = Matrix4.translationValues(size.width / 2 - 30, size.height / 2 - 30, 0);
      context.paintChild(2, transform: m1);
      Matrix4 m2 = Matrix4.translationValues(size.width / 2 - 30, -(size.height / 2 - 50), 0);
      context.paintChild(3, transform: m2);
    }
  }
  @override
  bool shouldRepaint(covariant SwipeFlowDelegate oldDelegate) {
    return oldDelegate.offsetX.value != offsetX.value;
  }
}

從這里可以看出,FlowDelegate 的最大優(yōu)勢是可以自定義孩子的繪制與否,還可以在繪制時通過 Matrix4 對孩子進(jìn)行矩陣變換,還有可選參數(shù)可以控制透明度。接下來使用 Flow 組件時,提供 SwipeFlowDelegate ,并在 children 列表中依次放入子組件。其中前兩個組件由外界傳入,分別是底組件和上層組件,這樣組件的布局就完成了,接下來監(jiān)聽事件,更新 factor 即可:

final ValueNotifier<double> factor = ValueNotifier(0);
Flow(
  delegate: SwipeFlowDelegate(factor),
  children: [
    widget.content,
    widget.overflow,
    GestureDetector(
        onTap: open,
        child: const Icon(Icons.menu_open_outlined, color: Colors.white)),
    GestureDetector(
        onTap: () {
          Navigator.of(context).pop();
        },
        child: const Icon(Icons.close, color: Colors.white))
  ],
)

手勢的監(jiān)聽

這里手勢的處理是非常簡單的,通過 GestureDetector 監(jiān)聽水平拖拽事件。在 onHorizontalDragUpdate 中根據(jù)拖拽的偏移量更新 factor 的值,其中通過 .clamp(0, widget.width) 可以限制偏移量的取值區(qū)間。

@override
Widget build(BuildContext context) {
  return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onHorizontalDragUpdate: _onHorizontalDragUpdate,
      onHorizontalDragEnd: _onHorizontalDragEnd,
      child: SizedBox(
        height: MediaQuery.of(context).size.height,
        width: widget.width,
        child: Flow( delegate:// 同上,略...
      );
}
void _onHorizontalDragUpdate(DragUpdateDetails details) {
  double cur = factor.value + details.delta.dx;
  factor.value = cur.clamp(0, widget.width);
}
void _onHorizontalDragEnd(DragEndDetails details) {
  if (factor.value > widget.width / 2) {
    close();
  } else {
    open();
  }
}

最后在 _onHorizontalDragEnd 回調(diào)中,根據(jù)當(dāng)前偏移量是否大于一般寬度,決定關(guān)閉還是打開。期間過程使用動畫進(jìn)行偏移量的過渡變化。

動畫的使用

動畫的使用,主要是通過 AnimationController 動畫控制器來驅(qū)動數(shù)值的變化;在放手時 Tween 創(chuàng)建補(bǔ)間動畫器,監(jiān)聽動畫器數(shù)值的變化更新偏移量。這樣偏移量就可以在指定時間內(nèi),在兩個值之間漸變,從而產(chǎn)生動畫效果。比如抬手時,open 方法是讓偏移量從當(dāng)前位置變化到 0 :

class _ScrollHideWrapperState extends State<ScrollHideWrapper> with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  final ValueNotifier<double> factor = ValueNotifier(0);
  @override
  void initState() {
    super.initState();
    _ctrl = AnimationController(
      duration: const Duration(milliseconds: 200),
      vsync: this,
    );
  }
  @override
  Widget build(BuildContext context) {
    // 略同...
  }
  // 動畫關(guān)閉
  Future<void> close() async {
    Animation<double> anim = Tween<double>(begin: factor.value, end: widget.width).animate(_ctrl);
    anim.addListener(() => factor.value = anim.value);
    await _ctrl.forward(from: 0);
  }
  // 動畫打開
  Future<void> open() async {
    Animation<double> anim = Tween<double>(begin: factor.value, end: 0).animate(_ctrl);
    anim.addListener(() => factor.value = anim.value);
    await _ctrl.forward(from: 0);
  }
}

如果想讓動畫的變化非勻速,可以使用 Curve 來控制動畫曲線。這樣,基于 Flow 實現(xiàn)的自定義布局,就可以根據(jù)手勢和動畫,完成特定的交互功能。從這里可以看出 Flow 自定義布局的靈活性非常強(qiáng),很多疑難雜癥,都可以使用它來完成。

感謝各位的閱讀,以上就是“Flutter Flow如何實現(xiàn)滑動顯隱層”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Flutter Flow如何實現(xiàn)滑動顯隱層這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI