mm_swipeable 1.0.0+1 copy "mm_swipeable: ^1.0.0+1" to clipboard
mm_swipeable: ^1.0.0+1 copied to clipboard

MmSwipeable is a Flutter package that provides a widget for enabling swipe actions in both left and right directions.

example/lib/main.dart

import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mm_swipeable/mm_swipeable.dart';

void main() {
  runApp(const MyApp());
}

class Cat {
  final int id;
  final String name;
  final String about;
  final String imageUrl;

  const Cat({
    required this.id,
    required this.name,
    required this.about,
    required this.imageUrl,
  });

  factory Cat.random1() {
    return Cat(
      id: Random().nextInt(10000),
      name: 'Fluffy',
      about: 'Cute and adorable',
      imageUrl:
          'https://images.pexels.com/photos/2071882/pexels-photo-2071882.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
    );
  }

  factory Cat.random2() {
    return Cat(
      id: Random().nextInt(10000),
      name: 'Whiskers',
      about: 'Loves to play',
      imageUrl:
          'https://images.pexels.com/photos/208984/pexels-photo-208984.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
    );
  }

  factory Cat.random3() {
    return Cat(
      id: Random().nextInt(10000),
      name: 'Mittens',
      about: 'Very friendly',
      imageUrl:
          'https://images.pexels.com/photos/1521304/pexels-photo-1521304.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
    );
  }
}

class CatProvider extends ChangeNotifier {
  final Map<Cat, MmSwipeableController> catsAndControllers = {};

  void initCats() {
    for (var i = 0; i < 20; i++) {
      catsAndControllers[Cat.random1()] = MmSwipeableController();
      catsAndControllers[Cat.random2()] = MmSwipeableController();
      catsAndControllers[Cat.random3()] = MmSwipeableController();
    }
    notifyListeners();
  }

  void removeCat(Cat cat) {
    catsAndControllers.remove(cat);
    notifyListeners();
  }

  @override
  void dispose() {
    for (final controller in catsAndControllers.values) {
      controller.dispose();
    }
    super.dispose();
  }
}

final catProvider = CatProvider()..initCats();

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'MmSwipeable Demo',
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        centerTitle: true,
        title: Image.asset(
          'assets/makromusic_logo_with_text.png',
          height: 30,
        ),
      ),
      body: ListenableBuilder(
        listenable: catProvider,
        builder: (context, _) {
          final catsAndControllers = catProvider.catsAndControllers;
          if (catsAndControllers.isEmpty) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
          final length = catsAndControllers.length;
          final bottom = catsAndControllers.entries.elementAt(length - 2);
          final top = catsAndControllers.entries.elementAt(length - 1);

          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
            child: Column(
              children: [
                Expanded(
                  child: Stack(
                    children: [
                      CatCard(
                        key: ValueKey(bottom.key.id),
                        controller: bottom.value,
                        cat: bottom.key,
                      ),
                      CatCard(
                        key: ValueKey(top.key.id),
                        controller: top.value,
                        cat: top.key,
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 20),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    Expanded(
                      child: GestureDetector(
                        onTap: () {
                          catsAndControllers.values.last.swipeLeft();
                        },
                        child: Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 16,
                            vertical: 8,
                          ),
                          decoration: const BoxDecoration(
                            borderRadius: BorderRadius.only(
                              topLeft: Radius.circular(24),
                              bottomLeft: Radius.circular(24),
                              topRight: Radius.circular(6),
                              bottomRight: Radius.circular(6),
                            ),
                            color: Color(0xFF0F141E),
                          ),
                          child: const Text(
                            'Nope',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                            ),
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: GestureDetector(
                        onTap: () {
                          catsAndControllers.values.last.swipeRight();
                        },
                        child: Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 16,
                            vertical: 8,
                          ),
                          decoration: const BoxDecoration(
                            borderRadius: BorderRadius.only(
                              topLeft: Radius.circular(6),
                              bottomLeft: Radius.circular(6),
                              topRight: Radius.circular(24),
                              bottomRight: Radius.circular(24),
                            ),
                            color: Color(0xFF42C0C6),
                          ),
                          child: const Text(
                            'Like',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ],
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

class CatCard extends StatefulWidget {
  final MmSwipeableController controller;
  final Cat cat;

  const CatCard({
    super.key,
    required this.controller,
    required this.cat,
  });

  @override
  State<CatCard> createState() => _CatCardState();
}

class _CatCardState extends State<CatCard> {
  double leftTextOpacity = 0;
  double rightTextOpacity = 0;

  @override
  void initState() {
    widget.controller.addListener(updateOpacity);
    super.initState();
  }

  @override
  void dispose() {
    widget.controller.removeListener(updateOpacity);
    super.dispose();
  }

  void updateOpacity() {
    final angle = widget.controller.value.angle;
    setState(() {
      leftTextOpacity = clampDouble(-angle, 0, 1);
      rightTextOpacity = clampDouble(angle, 0, 1);
    });
  }

  void remove(Cat matchData) {
    catProvider.removeCat(matchData);
  }

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: catProvider,
      builder: (context, _) {
        return MmSwipeable(
          controller: widget.controller,
          actionOffsetDuration: const Duration(milliseconds: 250),
          swipeAnimationDuration: const Duration(milliseconds: 2000),
          resetAnimationDuration: const Duration(milliseconds: 1200),
          confirmSwipe: () {
            final value = widget.controller.value;
            final angle = value.angle.abs();
            final force = value.force.abs();
            if (angle <= 0.7 && force <= 0.3) {
              return null;
            }
            return true;
          },
          onSwipedRight: () {
            remove(widget.cat);
            // Do anything you want here
          },
          onSwipedLeft: () {
            remove(widget.cat);
            // Do anything you want here
          },
          onSwipeLeftCancelled: () {
            // Do anything you want here
          },
          onSwipeRightCancelled: () {
            // Do anything you want here
          },
          child: Stack(
            children: [
              ClipRRect(
                borderRadius: BorderRadius.circular(16),
                child: SizedBox(
                  height: MediaQuery.of(context).size.height * .8,
                  child: Image.network(
                    widget.cat.imageUrl,
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              Positioned.fill(
                child: Container(
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(16),
                    gradient: LinearGradient(
                      begin: Alignment.bottomCenter,
                      end: Alignment.topCenter,
                      colors: [
                        Colors.black.withOpacity(0.8),
                        Colors.transparent,
                      ],
                    ),
                  ),
                  child: Padding(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 16.0,
                      vertical: 24,
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisAlignment: MainAxisAlignment.end,
                      children: [
                        Row(
                          children: [
                            ClipRRect(
                              borderRadius: BorderRadius.circular(100),
                              child: SizedBox(
                                height: 50,
                                width: 50,
                                child: Image.network(
                                  widget.cat.imageUrl,
                                  fit: BoxFit.cover,
                                ),
                              ),
                            ),
                            const SizedBox(width: 12),
                            Text(
                              widget.cat.name,
                              style: const TextStyle(
                                color: Colors.white,
                                fontSize: 24,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 8),
                        Text(
                          widget.cat.about,
                          style: const TextStyle(
                            color: Colors.white,
                            fontSize: 16,
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(
                  vertical: 12.0,
                  horizontal: 24,
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    AnimatedOpacity(
                      opacity: rightTextOpacity,
                      duration: Duration.zero,
                      child: Container(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 16,
                          vertical: 8,
                        ),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(8),
                          color: const Color(0xFF42C0C6),
                        ),
                        child: const Text(
                          'Like',
                          style: TextStyle(
                            fontSize: 24,
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                          ),
                        ),
                      ),
                    ),
                    AnimatedOpacity(
                      opacity: leftTextOpacity,
                      duration: Duration.zero,
                      child: Container(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 16,
                          vertical: 8,
                        ),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(8),
                          color: const Color(0xFF0F141E),
                        ),
                        child: const Text(
                          'Nope',
                          style: TextStyle(
                            fontSize: 24,
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}
11
likes
150
points
0
downloads

Publisher

unverified uploader

Weekly Downloads

MmSwipeable is a Flutter package that provides a widget for enabling swipe actions in both left and right directions.

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on mm_swipeable