5)Learning diary for flutter

No update for a long time.

This is about an interesting e-reader.

At present,The function is no prefect.

The main part is the widget be used to display the book content.

The code about this part seems  a little  long 

So in order to look more concise,first delete the detailed code 

The widget implements the path  calculation  according to the position of the finger.

The class pageClipper display the area of the page according to the path.


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


class BookWidget extends StatefulWidget {
  BookWidgetData data;
  bool enable;
  var callBack;
  var onFingerDown;

  BookWidget(this.data, this.enable, func(bool isNext, int page),
      func2(bool isNext, int page))
      : super() {
    print(data.bgStr);
    print(data.colorpos);
    callBack = func;
    onFingerDown = func2;
  }

  State state;

  State<StatefulWidget> createState() {
    state = BookWidgetState();
    return state;
  }

  void upData(BookWidgetData data) {
    if(data.page==-1||data.page==-2){
      data.bgStr="images/bg.jpeg";
    }
    (state as BookWidgetState).upData(data);
  }
}


class BookWidgetState extends State<BookWidget>  {
 

  void upData(BookWidgetData data) {
//    print("更新頁面方法"+ widget.data.content);

    setState(() {
      widget.data = data;
//      widget.color = data.color;
//      widget.colorPos = data.colorpos;
      reset();
    });
//    print("更新頁面方法end"+ widget.data.content);
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    isright = false;
    isleft = false;
    reset();
  }

  @override
  Widget build(BuildContext context) {
//    width=context.size.width;
//    height=context.size.height;
 

    return Listener(
      child: Stack(
        fit: StackFit.expand,
        children: [
          ClipPath(
            clipper: pageCliper(getPathBFromLower),
            child: Container(
              color: Colors.white,
              child: Text(""),
            ),
          ),
          ClipPath(
              clipper: pageCliper(getPathAFromLower),
              child: Container(
//            color: widget.data.page==-1||widget.data.page==-1
//                ? Colors.white
//                : null,
                child: Padding(
                  padding: const EdgeInsets.only(top: 30, left: 5, right: 5),
                  child: Text(
                    widget.data.content,
                    style: TextStyle(
                        color: Colors.black87,
                        fontSize: 24,
                        decoration: TextDecoration.none),
                  ),
                ),
                decoration: BoxDecoration(
                    image: DecorationImage(
                            image: AssetImage(widget.data.bgStr != null?widget.data.bgStr:"images/bg.jpeg"),
                            fit: BoxFit.cover)
                        ),
              ))
        ],
      ),
      onPointerMove: widget.enable ? onFingerMove : null,
      onPointerUp: widget.enable ? onFingerUp : null,
      onPointerDown: widget.enable ? onFingerDown : null,
    );
  }

  void reset() {
   
  }

  void onFingerDown(PointerDownEvent event) {
   
  }

  void onFingerUp(PointerUpEvent event) {

  }

  void onFingerMove(PointerMoveEvent details) {
   
  }

  Path getPathAFromLower(size) {
 
  }

  Path getPathAFromLowerLeft(Size size) {
   
  }

  Path getPathAFromLowerRight(Size size) {
   
  }

  Path getPathBFromLowerRight(Size size) {
   
  }

  Path getPathBFromLowerLeft(Size size) {
    
  }

 



class pageCliper extends CustomClipper<Path> {
  var getPath;

  pageCliper(fun(Size size)) : super() {
    this.getPath = fun;
  }

  @override
  Path getClip(Size size) {
//    print("重繪");
    return getPath(size);
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    // TODO: implement shouldReclip
    return true;
  }
}

Here is the complete code

The path algorithm draws on a blog about Android's native development of e-book source code

It has been modified and only a few of them are used to achieve the same effect of turning left and right pages


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

class BookWidgetData {
  String content;
  int page;
  Color color;
  String bgStr;
  int colorpos;

  BookWidgetData();

  BookWidgetData.name(this.content, this.page);

  Object get colorPos => null;
}

class BookWidget extends StatefulWidget {
  BookWidgetData data;
  bool enable;
  var callBack;
  var onFingerDown;

  BookWidget(this.data, this.enable, func(bool isNext, int page),
      func2(bool isNext, int page))
      : super() {
    print(data.bgStr);
    print(data.colorpos);
    callBack = func;
    onFingerDown = func2;
  }

  State state;

  State<StatefulWidget> createState() {
    state = BookWidgetState();
    return state;
  }

  void upData(BookWidgetData data) {
    if(data.page==-1||data.page==-2){
      data.bgStr="images/bg.jpeg";
    }
    (state as BookWidgetState).upData(data);
  }
}

bool isright, isleft;

class BookWidgetState extends State<BookWidget> with TickerProviderStateMixin {
  Point a, f, g, e, h, c, j, b, k, d, i;
  double width, height;
  double lPathAShadowDis, rPathAShadowDis;
  AnimationController animationController;
  AnimationController animationController2;
  Animation<double> animation;

  void upData(BookWidgetData data) {
//    print("更新頁面方法"+ widget.data.content);

    setState(() {
      widget.data = data;
//      widget.color = data.color;
//      widget.colorPos = data.colorpos;
      reset();
    });
//    print("更新頁面方法end"+ widget.data.content);
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    isright = false;
    isleft = false;
    reset();
  }

  double startx, starty, endx;
  double scale;

  void startAnimal() {
//    count++;
    startx = a.x;
    starty = a.y;
    if (endx == width) {
      scale = (height - a.y) / (width - a.x);
    } else {
      scale = (height - a.y) / a.x;
    }
//    print("調用了一次$count");
    f.x = width;
    f.y = height;
    animation = new Tween(begin: startx, end: endx).animate(animationController)
      ..addListener(() {
        setState(() {
          a.x = animation.value;
          double add = animation.value - startx;
//          print(widget.data.content +
//              "增長值$add" +
//              "比率$scale" +
//              "stratX開始值$startx startY開始值");
          if (endx == width) {
            a.y = starty + add * scale;
          } else {
            a.y = starty - add * scale;
          }
          if (isleft) {
            a.x = width - a.x;
          }
          calcPointsXY(a, f);
        });
      })
      ..addStatusListener((AnimationStatus state) {
        if(state==AnimationStatus.dismissed){
          setState(() {
            reset();
          });

        }
        if (state == AnimationStatus.completed) {
//         animationController.reset();
          setState(() {
            reset();
          });
        }
      });
    animationController.forward();
  }

  void startAnimal2() {
//    count++;
    startx = a.x;
    starty = a.y;
    if (endx == width) {
      scale = (height - a.y) / (width - a.x);
    } else {
      scale = (height - a.y) / a.x;
    }
//    print("調用了一次$count");

    f.x = width;
    f.y = height;
    animation =
        new Tween(begin: startx, end: endx).animate(animationController2)
          ..addListener(() {
            setState(() {
              a.x = animation.value;
              double add = animation.value - startx;
//              print(widget.data.content +
//                  "增長值$add" +
//                  "比率$scale" +
//                  "stratX開始值$startx startY開始值");
              if (endx == width) {
                a.y = starty + add * scale;
              } else {
                a.y = starty - add * scale;
              }
              if (isleft) {
                a.x = width - a.x;
              }
//              print("x值");
//              print(a.x);
              calcPointsXY(a, f);
            });
          })
          ..addStatusListener((AnimationStatus state) {
            if(state==AnimationStatus.dismissed){
              setState(() {
                reset();
              });

            }
            if (state == AnimationStatus.completed) {
//         animationController.reset();
              setState(() {
                reset();
              });

              if (isright) {
                widget.callBack(true, widget.data.page);
              } else {
                widget.callBack(false, widget.data.page);
              }
            }
          });
    animationController2.forward();
  }

  @override
  Widget build(BuildContext context) {
//    width=context.size.width;
//    height=context.size.height;
    animationController = new AnimationController(
        duration: const Duration(milliseconds: 500), vsync: this);

    animationController2 = new AnimationController(
        duration: const Duration(milliseconds: 500), vsync: this);

    return Listener(
      child: Stack(
        fit: StackFit.expand,
        children: [
          ClipPath(
            clipper: pageCliper(getPathBFromLower),
            child: Container(
              color: Colors.white,
              child: Text(""),
            ),
          ),
          ClipPath(
              clipper: pageCliper(getPathAFromLower),
              child: Container(
//            color: widget.data.page==-1||widget.data.page==-1
//                ? Colors.white
//                : null,
                child: Padding(
                  padding: const EdgeInsets.only(top: 30, left: 5, right: 5),
                  child: Text(
                    widget.data.content,
                    style: TextStyle(
                        color: Colors.black87,
                        fontSize: 24,
                        decoration: TextDecoration.none),
                  ),
                ),
                decoration: BoxDecoration(
                    image: DecorationImage(
                            image: AssetImage(widget.data.bgStr != null?widget.data.bgStr:"images/bg.jpeg"),
                            fit: BoxFit.cover)
                        ),
              ))
        ],
      ),
      onPointerMove: widget.enable ? onFingerMove : null,
      onPointerUp: widget.enable ? onFingerUp : null,
      onPointerDown: widget.enable ? onFingerDown : null,
    );
  }

  void reset() {
    a = Point();
    a.x = -1;
    a.y = -1;
    f = Point();
    g = Point();
    e = Point();
    h = Point();
    c = Point();
    j = Point();
    b = Point();
    k = Point();
    d = Point();
    i = Point();
  }

  void onFingerDown(PointerDownEvent event) {
    isright = false;
    isleft = false;
    if (event.position.dx > width - 100 && event.position.dy > height - 100) {
      isright = true;
      widget.onFingerDown(true, widget.data.page);
    } else if (event.position.dx < 100 && event.position.dy > height - 100) {
      isleft = true;
      widget.onFingerDown(false, widget.data.page);
    }
  }

  void onFingerUp(PointerUpEvent event) {

    a.x = event.position.dx;
    a.y = event.position.dy;
    if (isright && a.x < 100) {
//      reset();
      endx =0;
      a.x = event.position.dx;
      a.y = event.position.dy;
      if(a.x==0){
        a.x=1;
      }
      startAnimal2();
//      widget.callBack(true, widget.data.page);
    } else if (isleft && a.x > width - 100) {
//      reset();
      endx = width;
      a.x = event.position.dx;
      a.y = event.position.dy;
      startAnimal2();
//      widget.callBack(false, widget.data.page);
    } else if(isright||isleft){
      reset();
      a.x = event.position.dx;
      a.y = event.position.dy;
      if (isright)
        endx = width;
      else
        endx = 0;
      startAnimal();
//      setState(() {
//        reset();
//      });
    }
  }

  void onFingerMove(PointerMoveEvent details) {
    if (isleft || isright) {
      setState(() {
        a.x = details.localPosition.dx;
        a.y = details.localPosition.dy;
        f.x = width;
        f.y = height;
        if (isleft) {
          a.x = width - a.x;
        }
        calcPointsXY(a, f);
      });
    }
  }

  Path getPathAFromLower(size) {
    if (isright) {
      return getPathAFromLowerRight(size);
    } else {
      return getPathAFromLowerLeft(size);
    }
  }

  Path getPathAFromLowerLeft(Size size) {
    var pathA = Path();
    if (a.x == -1 && a.y == -1||c.x==null) {
      width = size.width;
      height = size.height;
      pathA.lineTo(0, size.height);
      pathA.lineTo(size.width, size.height);
      pathA.lineTo(size.width, 0);
      return pathA;
    }
    pathA.lineTo(size.width, size.height); //移動到右下角
    pathA.lineTo(size.width - c.x, c.y); //移動到c點
    pathA.quadraticBezierTo(
        size.width - e.x, e.y, size.width - b.x, b.y); //從c到b畫貝塞爾曲線,控制點爲e
    pathA.lineTo(width - a.x, a.y); //移動到a點
    pathA.lineTo(size.width - k.x, k.y); //移動到k點
    pathA.quadraticBezierTo(
        size.width - h.x, h.y, size.width - j.x, j.y); //從k到j畫貝塞爾曲線,控制點爲h
    pathA.lineTo(0, 0); //移動到左上角
    pathA.lineTo(size.width, 0);
    pathA.lineTo(size.width, size.height);
    return pathA;
  }

  Path getPathAFromLowerRight(Size size) {
    var pathA = Path();
    if (a.x == -1 && a.y == -1) {
      width = size.width;
      height = size.height;
      pathA.lineTo(0, size.height);
      pathA.lineTo(size.width, size.height);
      pathA.lineTo(size.width, 0);
      return pathA;
    }
    pathA.lineTo(0, size.height); //移動到左下角
    pathA.lineTo(c.x, c.y); //移動到c點
    pathA.quadraticBezierTo(e.x, e.y, b.x, b.y); //從c到b畫貝塞爾曲線,控制點爲e
    pathA.lineTo(a.x, a.y); //移動到a點
    pathA.lineTo(k.x, k.y); //移動到k點
    pathA.quadraticBezierTo(h.x, h.y, j.x, j.y); //從k到j畫貝塞爾曲線,控制點爲h
    pathA.lineTo(size.width, 0); //移動到右上角
    pathA.close(); //閉合區域
    return pathA;
  }

  Path getPathBFromLower(size) {
    if (isright) {
      return getPathBFromLowerRight(size);
    } else {
      return getPathBFromLowerLeft(size);
    }
  }

  Path getPathBFromLowerRight(Size size) {
    Path pathB = Path();
    if (a.x == -1) {
      return pathB;
    }
    pathB.moveTo(i.x, i.y); //移動到i點
    pathB.lineTo(d.x, d.y); //移動到d點
    pathB.lineTo(b.x, b.y); //移動到b點
    pathB.lineTo(a.x, a.y); //移動到a點
    pathB.lineTo(k.x, k.y); //移動到k點
    return pathB;
  }

  Path getPathBFromLowerLeft(Size size) {
    Path pathB = Path();
    if (a.x == -1) {
      return pathB;
    }
    print(size.width);
    pathB.moveTo(size.width - i.x, i.y); //移動到i點
    pathB.lineTo(size.width - d.x, d.y); //移動到d點
    pathB.lineTo(size.width - b.x, b.y); //移動到b點
    pathB.lineTo(size.width - a.x, a.y); //移動到a點
    pathB.lineTo(size.width - k.x, k.y); //移動到k點
    return pathB;
  }

  void calcPointAByTouchPoint() {
    double w0 = width - c.x;

    double w1 = (f.x - a.x).abs();
    double w2 = width * w1 / w0;
    a.x = (f.x - w2).abs();

    double h1 = (f.y - a.y).abs();
    double h2 = w2 * h1 / w1;
    a.y = (f.y - h2).abs();
  }

  /**
   * 計算各點座標
   * @param a
   * @param f
   */
  void calcPointsXY(Point a, Point f) {
    g.x = (a.x + f.x) / 2;
    g.y = (a.y + f.y) / 2;

    e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
    e.y = f.y;

    h.x = f.x;
    h.y = g.y - (f.x - g.x) * (f.x - g.x) / (f.y - g.y);

    c.x = e.x - (f.x - e.x) / 2;
    c.y = f.y;

    j.x = f.x;
    j.y = h.y - (f.y - h.y) / 2;

    b = getIntersectionPoint(a, e, c, j);
    k = getIntersectionPoint(a, h, c, j);

    d.x = (c.x + 2 * e.x + b.x) / 4;
    d.y = (2 * e.y + c.y + b.y) / 4;

    i.x = (j.x + 2 * h.x + k.x) / 4;
    i.y = (2 * h.y + j.y + k.y) / 4;

    //計算d點到ae的距離
    double lA = a.y - e.y;
    double lB = e.x - a.x;
    double lC = a.x * e.y - e.x * a.y;

    lPathAShadowDis =
        ((lA * d.x + lB * d.y + lC) / sqrt(pow(lA, 2) + pow(lB, 2))).abs();

    //計算i點到ah的距離
    double rA = a.y - h.y;
    double rB = h.x - a.x;
    double rC = a.x * h.y - h.x * a.y;
    rPathAShadowDis =
        ((rA * i.x + rB * i.y + rC) / sqrt(pow(rA, 2) + pow(rB, 2))).abs();
  }

  /**
   * 計算兩線段相交點座標
   * @param lineOne_My_pointOne
   * @param lineOne_My_pointTwo
   * @param lineTwo_My_pointOne
   * @param lineTwo_My_pointTwo
   * @return 返回該點
   */
  Point getIntersectionPoint(
      Point lineOne_My_pointOne,
      Point lineOne_My_pointTwo,
      Point lineTwo_My_pointOne,
      Point lineTwo_My_pointTwo) {
    double x1, y1, x2, y2, x3, y3, x4, y4;
    x1 = lineOne_My_pointOne.x;
    y1 = lineOne_My_pointOne.y;
    x2 = lineOne_My_pointTwo.x;
    y2 = lineOne_My_pointTwo.y;
    x3 = lineTwo_My_pointOne.x;
    y3 = lineTwo_My_pointOne.y;
    x4 = lineTwo_My_pointTwo.x;
    y4 = lineTwo_My_pointTwo.y;

    double pointX =
        ((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) /
            ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4));
    double pointY =
        ((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) /
            ((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4));

    return new Point.name(pointX, pointY);
  }

  /**
   * 計算C點的X值
   * @param a
   * @param f
   * @return
   */
  double calcPointCX(Point a, Point f) {
    Point g, e;
    g = new Point();
    e = new Point();
    g.x = (a.x + f.x) / 2;
    g.y = (a.y + f.y) / 2;

    e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
    e.y = f.y;

    return e.x - (f.x - e.x) / 2;
  }
}

class Point {
  double x;
  double y;

  Point();

  Point.name(this.x, this.y);
}

class pageCliper extends CustomClipper<Path> {
  var getPath;

  pageCliper(fun(Size size)) : super() {
    this.getPath = fun;
  }

  @override
  Path getClip(Size size) {
//    print("重繪");
    return getPath(size);
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    // TODO: implement shouldReclip
    return true;
  }
}

 

發佈了26 篇原創文章 · 獲贊 0 · 訪問量 8034
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章