繁体   English   中英

如何在 flutter 中创建类似 Tinder 的堆叠卡?

[英]How to create a Tinder-like stacked card in flutter?

我想在 flutter 中创建类似 tinder 的可滑动卡片,它可以在左右滑动时调用一些 function 但在 Flutter 中这样做似乎太复杂了。 现在我已经使用带有 Dismissible 的列表视图构建器实现了该功能,该功能按预期工作,我可以在用户滑动时通过 API 调用更新列表,但 UI 看起来并不那么吸引人,因为卡片在水平关闭时不会旋转. 在发布之前,我已经阅读了许多博客文章,虽然其中一些给出了实现这一点的想法,但是不可能使用像本例中那样使用堆栈创建一副牌这样的技术在后台更新列表。 https://github.com/geekruchika/FlutterCardSwipe

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

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

可以直接使用 package https://pub.dev/packages/flutter_tindercard
或参考源代码
在 output 中,您可以看到列表长度没有改变,您可以检测到向左或向右滑动

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

工作演示

在此处输入图像描述

output

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

你可以这样做

第一个板条箱TinderSwapCard class 并复制粘贴以下代码

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

之后像这样在屏幕上使用 class

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

在此处输入图像描述

我在同样的问题上苦苦挣扎,想使用flutter_tindercard package,但它很难定制,也不能很好地满足我的需求。 因此,我创建了swiping_card_deck package,它创建了一个小部件,用于通过手势或按钮在一副卡片中滑动。 它的界面设计简单易懂,默认设置应该适用于大多数用例。 它还在开发中,因此您可以为您想要的任何功能创建问题,甚至可以自己为 package 做出贡献。 这是一个直接从文档中提取的简单示例:

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

void main() {
  runApp(const MaterialApp(
    home: Scaffold(
        body: Center(
      child: ExamplePage(),
    )),
    title: 'SwipingCardDeck',
  ));
}

class ExamplePage extends StatelessWidget {
  const ExamplePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final SwipingCardDeck deck = SwipingCardDeck(
      cardDeck: getCardDeck(),
      onDeckEmpty: () => debugPrint("Card deck empty"),
      onLeftSwipe: (Card card) => debugPrint("Swiped left!"),
      onRightSwipe: (Card card) => debugPrint("Swiped right!"),
      cardWidth: 200,
    );
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        deck,
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          mainAxisSize: MainAxisSize.min,
          children: [
            IconButton(
              icon: const Icon(Icons.clear),
              iconSize: 30,
              color: Colors.red,
              onPressed: deck.animationActive
                  ? null
                  : () => deck.swipeLeft(MediaQuery.of(context).size),
            ),
            const SizedBox(width: 40),
            IconButton(
              icon: const Icon(Icons.check),
              iconSize: 30,
              color: Colors.green,
              onPressed: deck.animationActive
                  ? null
                  : () => deck.swipeRight(MediaQuery.of(context).size),
            ),
          ],
        ),
      ],
    );
  }

  List<Card> getCardDeck() {
    List<Card> cardDeck = [];
    for (int i = 0; i < 500; ++i) {
      cardDeck.add(
        Card(
          color: Color((math.Random().nextDouble() * 0xFFFFFF).toInt())
              .withOpacity(1.0),
          child: const SizedBox(height: 300, width: 200)),
      );
    }
    return cardDeck;
  }
}

你可以试试这个 flutter package swipe_cards

要安装 package,请将以下依赖项添加到您的pubspec.yaml

dependencies:
    swipe_cards: ^1.0.0

刷卡演示

参考:

  1. https://pub.dev/packages/swipe_cards
  2. https://github.com/AnupKumarPanwar/swipe_cards

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM