You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Flutter FlipWidget实现图书页面双向翻页功能?

Flutter实现图书阅读页面双向翻页功能

问题分析

你的现有代码仅支持向前翻页,核心问题有两点:

  • 拖动更新逻辑只处理了向左拖动(off.dx < 0)的情况,向右拖动时percent被强制设为0,无法触发反向翻页动画
  • _updatePageIndex方法仅单向递增页面索引,没有处理向后翻页的索引递减逻辑

修改方案

我们需要调整手势处理逻辑,同时支持左右拖动的动画计算,并根据拖动方向更新页面索引:

  1. 调整拖动更新逻辑:

    • 不再限制percent为非负值,而是根据拖动方向保留正负值,对应正反翻页
    • 适配向右拖动时的倾斜角度计算,保证动画自然
  2. 优化页面索引更新:

    • 记录拖动方向,在翻页结束时根据方向递增或递减索引,同时处理边界(第一页不能再减,最后一页不能再加)

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:flip_widget/flip_widget.dart';
import 'dart:math' as math;

class AchristmasCarol extends StatefulWidget {
  @override
  _AchristmasCarolState createState() => _AchristmasCarolState();
}

const double _MinNumber = 0.008;

double _clampMin(double v) {
  if (v < _MinNumber && v > -_MinNumber) {
    if (v >= 0) {
      v = _MinNumber;
    } else {
      v = -_MinNumber;
    }
  }
  return v;
}

class _AchristmasCarolState extends State<AchristmasCarol> {
  GlobalKey<FlipWidgetState> _flipKey = GlobalKey();

  Offset _oldPosition = Offset.zero;
  bool _visible = true;
  // 记录拖动方向,true为向前翻(左拖),false为向后翻(右拖)
  bool _isForwardFlip = true;

  List<String> pages = [
    "assets/pics/A Christmas Carol.jpg",
    "assets/books/A Christmas Carol/page 1.png",
    "assets/books/A Christmas Carol/page 2.png",
    "assets/books/A Christmas Carol/page 3.png",
    "assets/books/A Christmas Carol/page 4.png",
    "assets/books/A Christmas Carol/page 5.png",
    "assets/books/A Christmas Carol/page 6.png",
    "assets/books/A Christmas Carol/page 7.png",
    "assets/books/A Christmas Carol/page 8.png",
    "assets/books/A Christmas Carol/page 9.png",
    "assets/books/A Christmas Carol/page 10.png",
    // Add more pages as needed
  ];

  int currentPageIndex = 0;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    Size screenSize = MediaQuery.of(context).size;
    Size boxSize = Size(screenSize.width, screenSize.height);

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Visibility(
                child: Container(
                  width: boxSize.width,
                  height: boxSize.height,
                  child: GestureDetector(
                    child: FlipWidget(
                      key: _flipKey,
                      textureSize: boxSize * 3,
                      child: Container(
                        color: Color.fromARGB(255, 255, 255, 255),
                        child: Center(
                          child: Image.asset(
                            pages[currentPageIndex],
                            fit: BoxFit.cover,
                          ),
                        ),
                      ),
                    ),
                    onHorizontalDragStart: (details) {
                      _oldPosition = details.globalPosition;
                      _flipKey.currentState?.startFlip();
                    },
                    onHorizontalDragUpdate: (details) {
                      Offset off = details.globalPosition - _oldPosition;
                      _isForwardFlip = off.dx < 0; // 根据拖动方向标记翻页类型

                      // 调整percent计算,支持正负值对应正反翻页
                      double percent = off.dx / boxSize.width * -1.4;
                      // 适配向右拖动时的倾斜角度,保证动画自然
                      double tilt = 1 / _clampMin((_isForwardFlip ? -off.dy : off.dy) + 20 / 100);
                      percent = percent - percent / 2 * (1 - 1 / tilt);
                      _flipKey.currentState?.flip(percent, tilt);
                    },
                    onHorizontalDragEnd: (details) {
                      _flipKey.currentState?.stopFlip();
                      _updatePageIndex();
                    },
                    onHorizontalDragCancel: () {
                      _flipKey.currentState?.stopFlip();
                    },
                  ),
                ),
                visible: _visible,
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _updatePageIndex() {
    setState(() {
      if (_isForwardFlip) {
        // 向前翻页:索引递增,到达最后一页后停在当前页
        if (currentPageIndex < pages.length - 1) {
          currentPageIndex++;
        }
      } else {
        // 向后翻页:索引递减,到达第一页后停在当前页
        if (currentPageIndex > 0) {
          currentPageIndex--;
        }
      }
    });
  }
}

关键修改点说明

  • 新增_isForwardFlip变量记录拖动方向,用于后续索引更新判断
  • onHorizontalDragUpdate中:
    • 移除math.max(0, ...)限制,让percent支持正负值,对应正反翻页动画
    • 根据拖动方向调整tilt的计算逻辑,保证向右翻页时倾斜效果自然
  • _updatePageIndex方法根据_isForwardFlip的值,分别处理索引的递增和递减,同时添加边界判断,避免索引越界

内容的提问来源于stack exchange,提问作者user22653733

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:26:02