Skip to content

Commit

Permalink
v 1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
letsdoit07 committed Oct 22, 2021
1 parent a0317fc commit 4c643fc
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 49 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,9 @@
## [1.1.0] - October 22, 2021

* onSwipeLeft, onSwipeRight Callbacks
* onChange Callback that provides you with the Current Index
* cardSpreadInDegrees - to change the Spread Radius of Background Widgets [Min (2) , Max(10)]

## [1.0.0] - September 25, 2021

* Initial Release
Expand Down
59 changes: 40 additions & 19 deletions README.md
Expand Up @@ -5,7 +5,7 @@ A simple Flutter Package to Mimic iMessage Image Picker for Flutter
## Current Features

* Add List of Widgets to present them in a Swipable Deck
* Basic Customizations Widget
* Basic Widget Customizations

## Demo
![](https://github.com/retroportalstudio/swipe_deck/blob/master/swipe_deck.gif)
Expand All @@ -14,26 +14,47 @@ A simple Flutter Package to Mimic iMessage Image Picker for Flutter
To Use, simply Wrap the Widget you want to add Focused Menu to, with FocusedMenuHolder:
```dart
body: Container(
child: SwipeDeck(
startIndex: 3,
aspectRatio: 4 / 3,
emptyIndicator: Container(child: Center(child: Text("Nothing Here"),),),
widgets: IMAGES
.map((e) => GestureDetector(
onTap: () {
print(e);
},
child: ClipRRect(
borderRadius: borderRadius,
child: Image.asset(
"assets/images/$e.jpg",
fit: BoxFit.cover,
)),
))
.toList(),
),
child: SwipeDeck(
startIndex: 3,
emptyIndicator: Container(
child: Center(
child: Text("Nothing Here"),
),
),
cardSpreadInDegrees: 5, // Change the Spread of Background Cards
onSwipeLeft: (){
print("USER SWIPED LEFT -> GOING TO NEXT WIDGET");
},
onSwipeRight: (){
print("USER SWIPED RIGHT -> GOING TO PREVIOUS WIDGET");
},
onChange: (index){
print(IMAGES[index]);
},
widgets: IMAGES
.map((e) => GestureDetector(
onTap: () {
print(e);
},
child: ClipRRect(
borderRadius: borderRadius,
child: Image.asset(
"assets/images/$e.jpg",
fit: BoxFit.cover,
)),
))
.toList(),
),
),
```
## Social Handles 🎯
[Twitter](https://twitter.com/theretroportal)
[Instagram](https://www.instagram.com/retroportalstudio)
[Youtube](https://www.youtube.com/retroportalstudio)
[LinkedIn](https://www.linkedin.com/in/parasjainrps/)

## Found this useful? 💙👨‍💻
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/theretroportal)

## Roadmap
Plans to add more customizations.
Expand Down
19 changes: 16 additions & 3 deletions example/lib/main.dart
Expand Up @@ -30,12 +30,25 @@ class TestPage extends StatelessWidget {
),
body: Center(
child: Container(
width: 400,
width: 600,
child: Center(
child: SwipeDeck(
startIndex: 3,
aspectRatio: 4 / 3,
emptyIndicator: Container(child: Center(child: Text("Nothing Here"),),),
emptyIndicator: Container(
child: Center(
child: Text("Nothing Here"),
),
),
cardSpreadInDegrees: 5, // Change the Spread of Background Cards
onSwipeLeft: (){
print("USER SWIPED LEFT -> GOING TO NEXT WIDGET");
},
onSwipeRight: (){
print("USER SWIPED RIGHT -> GOING TO PREVIOUS WIDGET");
},
onChange: (index){
print(IMAGES[index]);
},
widgets: IMAGES
.map((e) => GestureDetector(
onTap: () {
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Expand Up @@ -141,7 +141,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.0.0"
version: "1.1.0"
term_glyph:
dependency: transitive
description:
Expand Down
2 changes: 2 additions & 0 deletions lib/constants.dart
@@ -0,0 +1,2 @@
const PI = 3.14;
const DEFAULT_SPREAD = 0.0872665;
6 changes: 6 additions & 0 deletions lib/data_holder.dart
@@ -1,8 +1,14 @@
import 'package:flutter/foundation.dart';
import 'constants.dart';

class TransformData extends ChangeNotifier {
bool _leftDrag = false;
double _tDelta = 0;
double spreadRadians = DEFAULT_SPREAD; // 5 Degrees

TransformData({required double spreadRadians}) {
this.spreadRadians = spreadRadians;
}

get isLeftDrag => this._leftDrag;

Expand Down
69 changes: 44 additions & 25 deletions lib/swipe_deck.dart
Expand Up @@ -3,38 +3,48 @@ library swipe_deck;
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:swipe_deck/constants.dart';
import 'package:swipe_deck/data_holder.dart';
import 'package:provider/provider.dart';

class SwipeDeck extends StatefulWidget {
final List<Widget> widgets;
final int startIndex;
final Widget emptyIndicator;
final double aspectRatio;
final double aspectRatio, cardSpreadInDegrees;
final Function(int)? onChange;
final Function? onSwipeRight, onSwipeLeft;

const SwipeDeck({Key? key, required this.widgets, this.startIndex = 0, this.emptyIndicator = const _NothingHere(), this.aspectRatio = 4 / 3}) : super(key: key);
const SwipeDeck(
{Key? key,
required this.widgets,
this.startIndex = 0,
this.emptyIndicator = const _NothingHere(),
this.aspectRatio = 4 / 3,
this.onChange,
this.cardSpreadInDegrees = 5.0,
this.onSwipeRight,
this.onSwipeLeft})
: super(key: key);

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

class _SwipeDeckState extends State<SwipeDeck> {
final borderRadius = BorderRadius.circular(20.0);
List<Widget> leftStackRaw = [], rightStackRaw = [];
List<MapEntry<int, dynamic>> leftStack = [], rightStack = [];
Widget? currentWidget, contestantImage, removedImage;
bool draggingLeft = false, onHold = false, beginDrag = false;
double transformLevel = 0, removeTransformLevel = 0;
double transformLevel = 0, removeTransformLevel = 0, spreadInRadians = DEFAULT_SPREAD;
Timer? stackTimer, repositionTimer;

@override
void dispose() {
super.dispose();
if (stackTimer != null) {
stackTimer!.cancel();
}
if (repositionTimer != null) {
repositionTimer!.cancel();
}
stackTimer?.cancel();
repositionTimer?.cancel();
}

@override
Expand All @@ -44,6 +54,8 @@ class _SwipeDeckState extends State<SwipeDeck> {
return;
}

spreadInRadians = widget.cardSpreadInDegrees.clamp(2.0, 10.0) * (PI / 180);

leftStackRaw = widget.widgets.sublist(widget.startIndex);
rightStackRaw = widget.widgets.sublist(0, widget.startIndex);

Expand Down Expand Up @@ -94,7 +106,7 @@ class _SwipeDeckState extends State<SwipeDeck> {
setState(() {});
});
Future.delayed(Duration(milliseconds: 200), () {
repositionTimer!.cancel();
repositionTimer?.cancel();
transformLevel = max(transformLevel, 0);
setState(() {
onHold = false;
Expand All @@ -110,12 +122,17 @@ class _SwipeDeckState extends State<SwipeDeck> {
);
}

postOnChange(index) {
if (widget.onChange != null) {
widget.onChange!(index);
}
}

@override
Widget build(BuildContext context) {
final borderRadius = BorderRadius.circular(20.0);
bool dragLimit = transformLevel > 0.8;
return ChangeNotifierProvider(
create: (BuildContext context) => TransformData(),
create: (BuildContext context) => TransformData(spreadRadians: spreadInRadians),
child: LayoutBuilder(builder: (context, constraints) {
final imageWidth = constraints.maxWidth / 2;
final imageHeight = widget.aspectRatio * imageWidth;
Expand Down Expand Up @@ -158,6 +175,7 @@ class _SwipeDeckState extends State<SwipeDeck> {
}
contestantImage = leftStack.first.value;
}
bool changed = false;
if (transformLevel > 0.8) {
removedImage = currentWidget;
if (draggingLeft) {
Expand All @@ -167,6 +185,8 @@ class _SwipeDeckState extends State<SwipeDeck> {
leftStackRaw.insert(0, currentWidget!);
currentWidget = rightStackRaw.last;
rightStackRaw.removeLast();

changed = true;
if (rightStackRaw.isNotEmpty) {
contestantImage = rightStackRaw.last;
}
Expand All @@ -177,10 +197,16 @@ class _SwipeDeckState extends State<SwipeDeck> {
rightStackRaw.add(currentWidget!);
currentWidget = leftStackRaw.first;
leftStackRaw.removeAt(0);

changed = true;
if (leftStackRaw.isNotEmpty) {
contestantImage = leftStackRaw.first;
}
}
if (changed) {
draggingLeft ? widget.onSwipeLeft?.call() : widget.onSwipeRight?.call();
postOnChange(rightStackRaw.length);
}
refreshLHStacks();
}
setState(() {});
Expand Down Expand Up @@ -247,8 +273,6 @@ class _SwipeDeckState extends State<SwipeDeck> {
}
}

const _ROTATION_DIFF = 0.0872665;

class _WidgetHolder extends StatefulWidget {
final double width, height;
final Widget image;
Expand All @@ -271,18 +295,13 @@ class _WidgetHolderState extends State<_WidgetHolder> {
childImage = Container(width: widget.width, height: widget.height, child: widget.image);
}

@override
void didUpdateWidget(covariant _WidgetHolder oldWidget) {
if (oldWidget.image != widget.image) {
super.didUpdateWidget(oldWidget);
}
}

@override
Widget build(BuildContext context) {
double finalRotation = (widget.index <= widget.lastIndex - 3) ? (3 * _ROTATION_DIFF) : ((widget.lastIndex - widget.index) * _ROTATION_DIFF);
bool isLeft = context.read<TransformData>().isLeftDrag;
double scaleDifferential = 0.05 * context.read<TransformData>().transformDelta;
TransformData transformData = context.watch<TransformData>();
double spread = transformData.spreadRadians;
double finalRotation = (widget.index <= widget.lastIndex - 3) ? (3 * spread) : ((widget.lastIndex - widget.index) * spread);
bool isLeft = transformData.isLeftDrag;
double scaleDifferential = 0.05 * transformData.transformDelta;
return Transform.scale(
scale: isLeft ? (widget.isLeft ? (1 - scaleDifferential) : 1 + scaleDifferential) : (widget.isLeft ? 1 + scaleDifferential : (1 - scaleDifferential)),
child: Transform(alignment: Alignment.bottomCenter, transform: Matrix4.rotationZ(widget.isLeft ? -finalRotation : finalRotation), child: childImage),
Expand All @@ -302,7 +321,7 @@ class __NothingHereState extends State<_NothingHere> {
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("Nothing Here!"),
child: const Text("Nothing Here!"),
),
);
}
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
@@ -1,6 +1,6 @@
name: swipe_deck
description: A simple Flutter Package to Mimic iMessage Image Picker for Flutter
version: 1.0.0
version: 1.1.0
homepage: https://github.com/retroportalstudio/swipe_deck.git

environment:
Expand Down

0 comments on commit 4c643fc

Please sign in to comment.