Flutter 如何在Flatter中创建类似tinder的堆叠卡片?

Flutter 如何在Flatter中创建类似tinder的堆叠卡片?,flutter,flutter-layout,swipecardview,Flutter,Flutter Layout,Swipecardview,我想在flatter中创建类似于swipeableinder的卡片,它可以在左或右滑动中调用一些函数,但在flatter中这样做似乎太复杂了。现在,我已经使用列表视图生成器和Dismissible实现了这一功能,该功能按预期工作,我可以在用户刷卡时通过API调用更新列表,但UI看起来没有那么吸引人,因为在水平解除时,卡不会旋转。在这里发布之前,我读了很多博客文章,其中一些文章给出了实现这一点的想法,但不可能在后台使用这些技术更新列表,如使用堆栈创建一副卡片,如本例所示 这是我目前与刷卡相关的代码

我想在flatter中创建类似于swipeableinder的卡片,它可以在左或右滑动中调用一些函数,但在flatter中这样做似乎太复杂了。现在,我已经使用列表视图生成器和Dismissible实现了这一功能,该功能按预期工作,我可以在用户刷卡时通过API调用更新列表,但UI看起来没有那么吸引人,因为在水平解除时,卡不会旋转。在这里发布之前,我读了很多博客文章,其中一些文章给出了实现这一点的想法,但不可能在后台使用这些技术更新列表,如使用堆栈创建一副卡片,如本例所示

这是我目前与刷卡相关的代码

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


import 'loader.dart';
import '../screens/profile_overview.dart';
import '../providers/user.dart';

class SwipeProfiles extends StatefulWidget {
  final double deviceHeight;
  final double deviceWidth;
  SwipeProfiles(this.deviceHeight, this.deviceWidth);
  @override
  _SwipeProfilesState createState() => _SwipeProfilesState();
}

class _SwipeProfilesState extends State<SwipeProfiles> {
  List _profiles = [];
  bool _isLoading = true;
  bool _gettingMoreProducts = false;

  _loadMoreProfiles() async {
    print('Reached end of list');
    if (_gettingMoreProducts) {
      print('Already getting products');
      return;
    }
    print('Firestore function called');
    _gettingMoreProducts = true;
    dynamic newProfiles =
        await Provider.of<User>(context, listen: false).getProfiles();
    _profiles.addAll(newProfiles);
    setState(() {});
    print('New Products added');
    _gettingMoreProducts = false;
  }

  @override
  void initState() {
    print('Getting new products from init state');
    Provider.of<User>(context, listen: false).getProfiles().then((profiles) {
      setState(() {
        _profiles = profiles;
        _isLoading = false;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        margin: EdgeInsets.all(10),
        height: widget.deviceHeight * 0.75,
        decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
        child: _isLoading
            ? Loader()
            : ListView.builder(
                itemCount: _profiles.length,
                itemBuilder: (context, index) {
                  if (index == _profiles.length - 1) {
                    _loadMoreProfiles();
                  }
                  return Dismissible(
                    key: UniqueKey(),
                    onDismissed: (direction) {
                      setState(() {
                        _profiles.removeAt(index);
                      });
                    },
                    background: Container(
                      color: Colors.red,
                      child: Icon(
                        Icons.cancel,
                        color: Colors.white,
                        size: 50,
                      ),
                    ),
                    secondaryBackground: Container(
                      color: Colors.green,
                      child: Icon(
                        Icons.check,
                        color: Colors.white,
                        size: 50,
                      ),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: ProfileOverview(_profiles[index],
                          widget.deviceHeight, widget.deviceWidth),
                    ),
                  );
                },
              ));
  }
}

导入“包装:颤振/材料.省道”;
导入“包:provider/provider.dart”;
导入“loader.dart”;
导入“../screens/profile_overview.dart”;
导入“../providers/user.dart”;
类SwipeProfiles扩展StatefulWidget{
最终双设备高度;
最终双设备宽度;
SwipeProfiles(this.deviceHeight,this.deviceWidth);
@凌驾
_SwipeProfilessState createState()=>\u SwipeProfilessState();
}
类_SwipeProfilesState扩展状态{
列表_profiles=[];
bool_isLoading=true;
bool(u gettingmore products=false);
_loadMoreProfiles()异步{
打印(“到达列表末尾”);
如果(获取更多产品){
打印(“已获得产品”);
返回;
}
打印('调用Firestore函数');
_gettingMoreProducts=true;
动态新配置文件=
等待Provider.of(context,listen:false).getProfiles();
_profiles.addAll(newProfiles);
setState((){});
打印(“新增产品”);
_gettingMoreProducts=false;
}
@凌驾
void initState(){
打印(“从初始状态获取新产品”);
Provider.of(context,listen:false).getProfiles().then((profiles){
设置状态(){
_外形=外形;
_isLoading=false;
});
});
super.initState();
}
@凌驾
小部件构建(构建上下文){
返回容器(
保证金:所有(10),
高度:widget.deviceHeight*0.75,
装饰:盒子装饰(边框半径:边框半径。圆形(10)),
子项:_正在加载
?装载机()
:ListView.builder(
itemCount:_profiles.length,
itemBuilder:(上下文,索引){
如果(索引==\u profiles.length-1){
_loadMoreProfiles();
}
可驳回的回报(
键:UniqueKey(),
onDismissed:(方向){
设置状态(){
_配置文件。删除(索引);
});
},
背景:集装箱(
颜色:颜色,红色,
子:图标(
图标。取消,
颜色:颜色,白色,
尺码:50,
),
),
第二背景:容器(
颜色:颜色。绿色,
子:图标(
图标。检查,
颜色:颜色,白色,
尺码:50,
),
),
孩子:填充(
填充:常数边集全部(8.0),
子:ProfileOverview(_profiles[index],
widget.deviceHeight,widget.deviceWidth),
),
);
},
));
}
}

您可以直接使用软件包
或参考源代码
在输出中,您可以看到列表长度没有改变,您可以检测向左或向右滑动

swipeCompleteCallback:
                (CardSwipeOrientation orientation, int index) {
              print(orientation.toString());
              if (orientation == CardSwipeOrientation.LEFT) {
                print("Card is LEFT swiping");
                print(welcomeImages.length);
              } else if (orientation == CardSwipeOrientation.RIGHT) {
                print("Card is RIGHT swiping");
                print(welcomeImages.length);
              }
            },
工作演示

输出

I/flutter (32086): CardSwipeOrientation.LEFT
I/flutter (32086): Card is LEFT swiping
I/flutter (32086): 3
I/flutter (32086): CardSwipeOrientation.RIGHT
I/flutter (32086): Card is RIGHT swiping
I/flutter (32086): 3
完整代码

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ExampleHomePage(),
    );
  }
}

class ExampleHomePage extends StatefulWidget {
  @override
  _ExampleHomePageState createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage>
    with TickerProviderStateMixin {
  List<String> welcomeImages = [
    "assets/welcome0.png",
    "assets/welcome1.png",
    "assets/welcome2.png",
  ];

  @override
  Widget build(BuildContext context) {
    CardController controller; //Use this to trigger swap.

    return new Scaffold(
      body: new Center(
        child: Container(
          height: MediaQuery.of(context).size.height * 0.6,
          child: new TinderSwapCard(
            orientation: AmassOrientation.BOTTOM,
            totalNum: welcomeImages.length,
            stackNum: 3,
            swipeEdge: 4.0,
            maxWidth: MediaQuery.of(context).size.width * 0.9,
            maxHeight: MediaQuery.of(context).size.width * 0.9,
            minWidth: MediaQuery.of(context).size.width * 0.8,
            minHeight: MediaQuery.of(context).size.width * 0.8,
            cardBuilder: (context, index) {
              print('index ${index}');
              return Card(
                child: Image.asset('${welcomeImages[index]}'),
              );
            },
            cardController: controller = CardController(),
            swipeUpdateCallback: (DragUpdateDetails details, Alignment align) {
              /// Get swiping card's alignment
              if (align.x < 0) {
                //print("Card is LEFT swiping");
              } else if (align.x > 0) {
                //print("Card is RIGHT swiping");
              }
            },
            swipeCompleteCallback:
                (CardSwipeOrientation orientation, int index) {
              print(orientation.toString());
              if (orientation == CardSwipeOrientation.LEFT) {
                print("Card is LEFT swiping");
                print(welcomeImages.length);
              } else if (orientation == CardSwipeOrientation.RIGHT) {
                print("Card is RIGHT swiping");
                print(welcomeImages.length);
              }
            },
          ),
        ),
      ),
    );
  }
}
导入“包装:颤振/材料.省道”;
进口“包装:flatter_tindercard/flatter_tindercard.dart”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
//此小部件是应用程序的根。
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“颤振演示”,
主题:主题数据(
主样本:颜色。蓝色,
),
主页:例如主页(),
);
}
}
类ExampleHomePage扩展StatefulWidget{
@凌驾
_示例HomePageState createState()=>\u示例HomePageState();
}
类\u示例HomePageState扩展状态
使用TickerProviderStateMixin{
列表图像=[
“assets/welcome0.png”,
“assets/welcome1.png”,
“assets/welcome2.png”,
];
@凌驾
小部件构建(构建上下文){
CardController;//使用此命令触发交换。
归还新脚手架(
正文:新中心(
子:容器(
高度:MediaQuery.of(上下文).size.height*0.6,
孩子:新的火绒盒(
方向:A辅助方向。底部,
totalNum:welcomeImages.length,
stackNum:3,
swipeEdge:4.0,
maxWidth:MediaQuery.of(context).size.width*0.9,
maxHeight:MediaQuery.of(context).size.width*0.9,
minWidth:MediaQuery.of(context).size.width*0.8,
minHeight:MediaQuery.of(context).size.width*0.8,
卡片生成器:(上下文,索引){
打印('index${index}');
回程卡(
子项:Image.asset(“${welcomeImages[index]}”),
);
},
cardController:controller=cardController(),
swipeUpdateCallback:(DragUpdateDetails详细信息,对齐对齐){
///获得刷卡的对齐
if(align.x<0){
//打印(“卡在左刷卡”);
}否则如果(align.x>0){
//打印(“刷卡正确”);
}
import 'package:flutter/material.dart';
import 'dart:math';

List<Size> _cardSizes = new List();
List<Alignment> _cardAligns = new List();

enum TriggerDirection { none, right, left, up, down }

/// A Tinder-Like Widget.
class TinderSwapCard extends StatefulWidget {
  CardBuilder _cardBuilder;
  int _totalNum;
  int _stackNum;
  int _animDuration;
  int _currentIndex = 0;
  double _swipeEdge;
  double _swipeEdgeVertical;
  bool _swipeUp;
  bool _swipeDown;
  bool _allowVerticalMovement;
  CardSwipeCompleteCallback swipeCompleteCallback;
  CardDragUpdateCallback swipeUpdateCallback;
  CardController cardController;

  @override
  _TinderSwapCardState createState() => _TinderSwapCardState();

  /// Constructor requires Card Widget Builder [cardBuilder] & your card count [totalNum]
  /// , option includes: stack orientation [orientation], number of card display in same time [stackNum]
  /// , [swipeEdge] is the edge to determine action(recover or swipe) when you release your swiping card
  /// it is the value of alignment, 0.0 means middle, so it need bigger than zero.
  /// , and size control params;
  TinderSwapCard({@required CardBuilder cardBuilder,
    @required int totalNum,
    AmassOrientation orientation = AmassOrientation.BOTTOM,
    int stackNum = 3,
    int animDuration = 800,
    int currentIndex = 0,
    double swipeEdge = 3.0,
    double swipeEdgeVertical = 8.0,
    bool swipeUp = false,
    bool swipeDown = false,
    double maxWidth,
    double maxHeight,
    double minWidth,
    double minHeight,
    bool allowVerticalMovement = true,
    this.cardController,
    this.swipeCompleteCallback,
    this.swipeUpdateCallback})
      : this._cardBuilder = cardBuilder,
        this._totalNum = totalNum,
        assert(stackNum > 1),
        this._stackNum = stackNum,
        this._animDuration = animDuration,
        assert(swipeEdge > 0),
        this._swipeEdge = swipeEdge,
        assert(swipeEdgeVertical > 0),
        this._swipeEdgeVertical = swipeEdgeVertical,
        this._swipeUp = swipeUp,
        this._swipeDown = swipeDown,
        assert(maxWidth > minWidth && maxHeight > minHeight),
        this._allowVerticalMovement = allowVerticalMovement {
    double widthGap = maxWidth - minWidth;
    double heightGap = maxHeight - minHeight;

    currentIndex = _currentIndex;
    _cardAligns = new List();
    _cardSizes = new List();

    for (int i = 0; i < _stackNum; i++) {
      _cardSizes.add(new Size(minWidth + (widthGap / _stackNum) * i,
          minHeight + (heightGap / _stackNum) * i));

      switch (orientation) {
        case AmassOrientation.BOTTOM:
          _cardAligns.add(
              new Alignment(0.0, (0.5 / (_stackNum - 1)) * (stackNum - i)));
          break;
        case AmassOrientation.TOP:
          _cardAligns.add(
              new Alignment(0.0, (-0.5 / (_stackNum - 1)) * (stackNum - i)));
          break;
        case AmassOrientation.LEFT:
          _cardAligns.add(
              new Alignment((-0.5 / (_stackNum - 1)) * (stackNum - i), 0.0));
          break;
        case AmassOrientation.RIGHT:
          _cardAligns.add(
              new Alignment((0.5 / (_stackNum - 1)) * (stackNum - i), 0.0));
          break;
      }
    }
  }
}

class _TinderSwapCardState extends State<TinderSwapCard>
    with TickerProviderStateMixin {
  Alignment frontCardAlign;
  AnimationController _animationController;
  int _currentFront;
  static TriggerDirection _trigger;

  Widget _buildCard(BuildContext context, int realIndex) {
    if (realIndex < 0) {
      return Container();
    }
    int index = realIndex - _currentFront;
    widget._currentIndex = realIndex - _currentFront;

    if (index == widget._stackNum - 1) {
      return Align(
        alignment: _animationController.status == AnimationStatus.forward
            ? frontCardAlign = CardAnimation
            .frontCardAlign(
            _animationController,
            frontCardAlign,
            _cardAligns[widget._stackNum - 1],
            widget._swipeEdge,
            widget._swipeUp,
            widget._swipeDown)
            .value
            : frontCardAlign,
        child: Transform.rotate(
            angle: (pi / 180.0) *
                (_animationController.status == AnimationStatus.forward
                    ? CardAnimation
                    .frontCardRota(
                    _animationController, frontCardAlign.x)
                    .value
                    : frontCardAlign.x),
            child: new SizedBox.fromSize(
              size: _cardSizes[index],
              child: widget._cardBuilder(
                  context, widget._totalNum - realIndex - 1),
            )),
      );
    }

    return Align(
      alignment: _animationController.status == AnimationStatus.forward &&
          (frontCardAlign.x > 3.0 ||
              frontCardAlign.x < -3.0 ||
              frontCardAlign.y > 3 ||
              frontCardAlign.y < -3)
          ? CardAnimation
          .backCardAlign(_animationController,
          _cardAligns[index], _cardAligns[index + 1])
          .value
          : _cardAligns[index],
      child: new SizedBox.fromSize(
        size: _animationController.status == AnimationStatus.forward &&
            (frontCardAlign.x > 3.0 ||
                frontCardAlign.x < -3.0 ||
                frontCardAlign.y > 3 ||
                frontCardAlign.y < -3)
            ? CardAnimation
            .backCardSize(_animationController,
            _cardSizes[index], _cardSizes[index + 1])
            .value
            : _cardSizes[index],
        child: widget._cardBuilder(context, widget._totalNum - realIndex - 1),
      ),
    );
  }

  List<Widget> _buildCards(BuildContext context) {
    List<Widget> cards = new List();
    for (int i = _currentFront; i < _currentFront + widget._stackNum; i++) {
      cards.add(_buildCard(context, i));
    }

    cards.add(new SizedBox.expand(
      child: new GestureDetector(
        onPanUpdate: (DragUpdateDetails details) {
          setState(() {
            if (widget._allowVerticalMovement == true) {
              frontCardAlign = new Alignment(
                  frontCardAlign.x +
                      details.delta.dx * 20 / MediaQuery
                          .of(context)
                          .size
                          .width,
                  frontCardAlign.y +
                      details.delta.dy *
                          30 /
                          MediaQuery
                              .of(context)
                              .size
                              .height);
            } else {
              frontCardAlign = new Alignment(
                  frontCardAlign.x +
                      details.delta.dx * 20 / MediaQuery
                          .of(context)
                          .size
                          .width,
                  0);

              if (widget.swipeUpdateCallback != null) {
                widget.swipeUpdateCallback(details, frontCardAlign);
              }
            }

            if (widget.swipeUpdateCallback != null) {
              widget.swipeUpdateCallback(details, frontCardAlign);
            }
          });
        },
        onPanEnd: (DragEndDetails details) {
          animateCards(TriggerDirection.none);
        },
      ),
    ));
    return cards;
  }

  animateCards(TriggerDirection trigger) {
    if (_animationController.isAnimating ||
        _currentFront + widget._stackNum == 0) {
      return;
    }
    _trigger = trigger;
    _animationController.stop();
    _animationController.value = 0.0;
    _animationController.forward();
  }

  void triggerSwap(TriggerDirection trigger) {
    animateCards(trigger);
  }

  // support for asynchronous data events
  @override
  void didUpdateWidget(covariant TinderSwapCard oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget._totalNum != oldWidget._totalNum) {
      _initState();
    }
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

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

  void _initState() {
    _currentFront = widget._totalNum - widget._stackNum;
    widget._currentIndex = widget._totalNum - widget._stackNum;

    frontCardAlign = _cardAligns[_cardAligns.length - 1];
    _animationController = new AnimationController(
        vsync: this, duration: Duration(milliseconds: widget._animDuration));
    _animationController.addListener(() => setState(() {}));
    _animationController.addStatusListener((AnimationStatus status) {
      int index = widget._totalNum - widget._stackNum - _currentFront;
      widget._currentIndex = index;
      if (status == AnimationStatus.completed) {
        CardSwipeOrientation orientation;
        if (frontCardAlign.x < -widget._swipeEdge)
          orientation = CardSwipeOrientation.LEFT;
        else if (frontCardAlign.x > widget._swipeEdge)
          orientation = CardSwipeOrientation.RIGHT;
        else if (frontCardAlign.y < -widget._swipeEdgeVertical)
          orientation = CardSwipeOrientation.UP;
        else if (frontCardAlign.y > widget._swipeEdgeVertical)
          orientation = CardSwipeOrientation.DOWN;
        else {
          frontCardAlign = _cardAligns[widget._stackNum - 1];
          orientation = CardSwipeOrientation.RECOVER;
        }
        if (widget.swipeCompleteCallback != null)
          widget.swipeCompleteCallback(orientation, widget._currentIndex);
        if (orientation != CardSwipeOrientation.RECOVER) changeCardOrder();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    widget.cardController?.addListener((trigger) => triggerSwap(trigger));

    return Stack(children: _buildCards(context));
  }

  changeCardOrder() {
    setState(() {
      _currentFront--;
      frontCardAlign = _cardAligns[widget._stackNum - 1];
    });
  }
}

typedef Widget CardBuilder(BuildContext context, int index);

enum CardSwipeOrientation { LEFT, RIGHT, RECOVER, UP, DOWN }

/// swipe card to [CardSwipeOrientation.LEFT] or [CardSwipeOrientation.RIGHT]
/// , [CardSwipeOrientation.RECOVER] means back to start.
typedef CardSwipeCompleteCallback = void Function(
    CardSwipeOrientation orientation, int index);

/// [DragUpdateDetails] of swiping card.
typedef CardDragUpdateCallback = void Function(
    DragUpdateDetails details, Alignment align);

enum AmassOrientation { TOP, BOTTOM, LEFT, RIGHT }

class CardAnimation {
  static Animation<Alignment> frontCardAlign(AnimationController controller,
      Alignment beginAlign,
      Alignment baseAlign,
      double swipeEdge,
      bool swipeUp,
      bool swipeDown) {
    double endX, endY;

    if (_TinderSwapCardState._trigger == TriggerDirection.none) {
      endX = beginAlign.x > 0
          ? (beginAlign.x > swipeEdge ? beginAlign.x + 10.0 : baseAlign.x)
          : (beginAlign.x < -swipeEdge ? beginAlign.x - 10.0 : baseAlign.x);
      endY = beginAlign.x > 3.0 || beginAlign.x < -swipeEdge
          ? beginAlign.y
          : baseAlign.y;

      if (swipeUp || swipeDown) {
        if (beginAlign.y < 0) {
          if (swipeUp)
            endY =
            beginAlign.y < -swipeEdge ? beginAlign.y - 10.0 : baseAlign.y;
        } else if (beginAlign.y > 0) {
          if (swipeDown)
            endY = beginAlign.y > swipeEdge ? beginAlign.y + 10.0 : baseAlign.y;
        }
      }
    } else if (_TinderSwapCardState._trigger == TriggerDirection.left) {
      endX = beginAlign.x - swipeEdge;
      endY = beginAlign.y + 0.5;
    }
    /* Trigger Swipe Up or Down */
    else if (_TinderSwapCardState._trigger == TriggerDirection.up ||
        _TinderSwapCardState._trigger == TriggerDirection.down) {
      var beginY =
      _TinderSwapCardState._trigger == TriggerDirection.up ? -10 : 10;

      endY = beginY < -swipeEdge ? beginY - 10.0 : baseAlign.y;

      endX = beginAlign.x > 0
          ? (beginAlign.x > swipeEdge ? beginAlign.x + 10.0 : baseAlign.x)
          : (beginAlign.x < -swipeEdge ? beginAlign.x - 10.0 : baseAlign.x);
    } else {
      endX = beginAlign.x + swipeEdge;
      endY = beginAlign.y + 0.5;
    }
    return new AlignmentTween(begin: beginAlign, end: new Alignment(endX, endY))
        .animate(
        new CurvedAnimation(parent: controller, curve: Curves.easeOut));
  }

  static Animation<double> frontCardRota(AnimationController controller, double beginRot) {
    return new Tween(begin: beginRot, end: 0.0).animate(
        new CurvedAnimation(parent: controller, curve: Curves.easeOut));
  }

  static Animation<Size> backCardSize(AnimationController controller, Size beginSize, Size endSize) {
    return new SizeTween(begin: beginSize, end: endSize).animate(
        new CurvedAnimation(parent: controller, curve: Curves.easeOut));
  }

  static Animation<Alignment> backCardAlign(AnimationController controller,
      Alignment beginAlign, Alignment endAlign) {
    return new AlignmentTween(begin: beginAlign, end: endAlign).animate(
        new CurvedAnimation(parent: controller, curve: Curves.easeOut));
  }
}

typedef TriggerListener = void Function(TriggerDirection trigger);

class CardController {
  TriggerListener _listener;

  void triggerLeft() {
    if (_listener != null) {
      _listener(TriggerDirection.left);
    }
  }

  void triggerRight() {
    if (_listener != null) {
      _listener(TriggerDirection.right);
    }
  }

  void triggerUp() {
    if (_listener != null) {
      _listener(TriggerDirection.up);
    }
  }

  void triggerDown() {
    if (_listener != null) {
      _listener(TriggerDirection.down);
    }
  }

  void addListener(listener) {
    _listener = listener;
  }

  void removeListener() {
    _listener = null;
  }
}
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:nb_utils/nb_utils.dart';
import 'package:prokit_flutter/main/utils/AppWidget.dart';

import 'TinderCard.dart';

class TinderHomePage extends StatefulWidget {
  static String tag = '/TinderHomePage';

  @override
  _TinderHomePageState createState() => _TinderHomePageState();
}

class _TinderHomePageState extends State<TinderHomePage> {
  bool isActive = false;

  //  Use this to trigger swap. (i.e to swipe the card to left or right)
  CardController controller = CardController();

  //  List is used to show data in the card.
  List<UserDetails> user = [
    UserDetails(name: 'Barkha Singh', image: 'https://wallpapercave.com/wp/wp4636827.jpg', age: 20, city: 'Navsari'),
    UserDetails(
        name: 'Mithali Parkar',
        image: 'https://2.bp.blogspot.com/-_mKKdFGGd-Y/WxEULk2hqxI/AAAAAAAAOFo/pirHeoRRakArw4aFJzkWUVmygr86V69bACLcBGAs/s1600/actress-mithila-palkar-photos-18.jpg',
        age: 27,
        city: 'Mumbai'),
    UserDetails(name: 'Ayush Mehra ', image: 'https://i2.wp.com/wikifamouspeople.com/wp-content/uploads/2019/02/Ayush-Mehra.jpg?resize=1060%2C794&ssl=1', age: 27, city: 'Mumbai'),
  ];

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: appBar(context, 'Tinder Card'),
      body: isActive == false
          ? Container(
              height: size.height,
              child: TinderSwapCard(
                // orientation where you want to show the stack
                orientation: AmassOrientation.RIGHT,
                // total number of cards
                totalNum: user.length,
                // from which index you want to show the card
                currentIndex: 1,
                // no of stacks you want to show in background
                stackNum: 3,
                // set the max and min width
                maxWidth: size.width * .9,
                maxHeight: size.height * 0.8,
                minWidth: size.width * 0.8,
                minHeight: size.height * 0.7,
                cardController: controller,
                // Restrict the stack from swiping downside if its false
                swipeDown: false,
                // Restrict the stack from swiping upside if its false
                swipeUp: false,
                swipeUpdateCallback: (DragUpdateDetails details, Alignment align) {
                  //  Get swiping card's alignment.
                  print(details.localPosition);
                  if (align.x < 0) {
                    //  Card is LEFT swiping.
                  } else if (align.x > 0) {
                    //  Card is RIGHT swiping
                  }
                },
                swipeCompleteCallback: (CardSwipeOrientation orientation, int index) {
                  // Get orientation & index of swiped card!
                  if ((index + 1) == user.length) {
                    setState(() {
                      isActive = true;
                    });
                  }
                },
                cardBuilder: (BuildContext context, int index) {
                  // Customize you card as per your need
                  return Stack(
                    children: <Widget>[
                      Container(
                          padding: EdgeInsets.all(15),
                          decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(25),
                              image: DecorationImage(
                                  image: FadeInImage(
                                    placeholder: Image.asset('images/LikeButton/image/grey.jpg').image,
                                    image: Image.network(user[index].image).image,
                                  ).image,
                                  fit: BoxFit.cover),
                              color: Colors.white),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.end,
                            mainAxisAlignment: MainAxisAlignment.end,
                            children: <Widget>[
                              Container(
                                alignment: Alignment.bottomLeft,
                                child: Row(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  mainAxisAlignment: MainAxisAlignment.start,
                                  children: <Widget>[
                                    Text(user[index].name, style: TextStyle(fontSize: 20, color: Colors.white)),
                                    Container(margin: EdgeInsets.only(left: 10), child: Text(user[index].age.toString(), style: TextStyle(fontSize: 20, color: Colors.white)))
                                  ],
                                ),
                              ),
                              Container(alignment: Alignment.bottomLeft, child: Text(user[index].city, style: TextStyle(fontSize: 18, color: Colors.white))),
                            ],
                          )),
                      Align(
                        alignment: Alignment.bottomLeft,
                        child: InkWell(
                          onTap: () {
                            controller.triggerLeft();
                          },
                          child: Container(
                              height: 65,
                              width: 75,
                              decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle),
                              margin: EdgeInsets.only(bottom: 100, left: 80),
                              child: Icon(Icons.clear, color: Colors.white, size: 50)),
                        ),
                      ),
                      Align(
                        alignment: Alignment.bottomRight,
                        child: InkWell(
                          onTap: () {
                            controller.triggerRight();
                          },
                          child: Container(
                            height: 65,
                            width: 75,
                            decoration: BoxDecoration(color: Colors.green, shape: BoxShape.circle),
                            margin: EdgeInsets.only(bottom: 100, right: 80),
                            child: Icon(Icons.check, color: Colors.white, size: 40),
                          ),
                        ),
                      ),
                    ],
                  );
                },
              ))
          : Container(
              width: size.width,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisSize: MainAxisSize.max,
                children: <Widget>[
                  Lottie.asset('images/Tinder/images/empty.json'),
                  SizedBox(height: 30),
                  Text('No Data Found', style: TextStyle(fontSize: 24, color: Color(0xFF757575))),
                ],
              ),
            ),
      floatingActionButton: isActive == true
          ? FloatingActionButton(
              onPressed: () {
                setState(() {
                  isActive = false;
                });
              },
              child: Icon(
                Icons.refresh,
                color: white,
              ),
            )
          : SizedBox(),
    );
  }
}

class UserDetails {
  String name;
  String image;
  int age;
  String city;

  UserDetails({
    this.name,
    this.image,
    this.age,
    this.city,
  });
}