diff --git a/CHANGELOG.md b/CHANGELOG.md index 7931666a..e47b7a3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.8 +### Addded +* onSlideAnimationChanged and onSlideIsOpenChanged on `SlidableController`. + ## 0.4.7 ### Fixed * https://github.com/letsar/flutter_slidable/issues/31 (Issue with dismiss animation). diff --git a/README.md b/README.md index ecab99ca..b8108a0b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_slidable: "^0.4.7" + flutter_slidable: "^0.4.8" ``` In your library add the following import: @@ -233,7 +233,32 @@ new Slidable( controller: slidableController, ... ); -``` +``` + +#### How can I animate an external widget at the same time as the active Slidable? + +You have to set the callbacks of a `SlidableController` instance: +The `onSlideAnimationChanged` let you get the animation of the current Slidable. +The `onSlideIsOpenChanged` let you know when the current Slidable opens and closes. + +```dart +final SlidableController slidableController = new SlidableController( + onSlideAnimationChanged: handleSlideAnimationChanged, + onSlideIsOpenChanged: handleSlideIsOpenChanged, + ); +... + void handleSlideAnimationChanged(Animation slideAnimation) { + setState(() { + _rotationAnimation = slideAnimation; + }); + } + + void handleSlideIsOpenChanged(bool isOpen) { + setState(() { + _fabColor = isOpen ? Colors.green : Colors.blue; + }); + } +``` ## Changelog diff --git a/example/lib/main.dart b/example/lib/main.dart index 5a199ff5..3ffbbe60 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -27,7 +27,7 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - final SlidableController slidableController = new SlidableController(); + SlidableController slidableController; final List<_HomeItem> items = List.generate( 20, (i) => new _HomeItem( @@ -38,6 +38,30 @@ class _MyHomePageState extends State { ), ); + @protected + void initState() { + slidableController = new SlidableController( + onSlideAnimationChanged: handleSlideAnimationChanged, + onSlideIsOpenChanged: handleSlideIsOpenChanged, + ); + super.initState(); + } + + Animation _rotationAnimation; + Color _fabColor = Colors.blue; + + void handleSlideAnimationChanged(Animation slideAnimation) { + setState(() { + _rotationAnimation = slideAnimation; + }); + } + + void handleSlideIsOpenChanged(bool isOpen) { + setState(() { + _fabColor = isOpen ? Colors.green : Colors.blue; + }); + } + @override Widget build(BuildContext context) { return new Scaffold( @@ -52,7 +76,17 @@ class _MyHomePageState extends State { ? Axis.vertical : Axis.horizontal), ), - ), // This trailing comma makes auto-formatting nicer for build methods. + ), + floatingActionButton: FloatingActionButton( + backgroundColor: _fabColor, + onPressed: null, + child: _rotationAnimation == null + ? Icon(Icons.add) + : RotationTransition( + turns: _rotationAnimation, + child: Icon(Icons.add), + ), + ), ); } diff --git a/example/lib/main_34.dart b/example/lib/main_34.dart new file mode 100644 index 00000000..be180a26 --- /dev/null +++ b/example/lib/main_34.dart @@ -0,0 +1,94 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +class Data { + final String url = 'https://via.placeholder.com/350x150'; +} + +void main() => runApp(new MyApp()); + +Future getData() async { + await Future.delayed(Duration(seconds: 1)); + return Data(); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Flutter Slidable Demo', + theme: new ThemeData( + primarySwatch: Colors.blue, + ), + home: new MyHomePage(title: 'Flutter Slidable Demo'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => new _MyHomePageState(); +} + +class _MyHomePageState extends State { + final SlidableController slidableController = new SlidableController(); + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text(widget.title), + ), + body: ListView.builder( + itemCount: 20, + itemBuilder: (context, index) { + return FutureBuilder( + future: getData(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Slidable( + delegate: SlidableScrollDelegate(), + actionExtentRatio: 0.25, + secondaryActions: [ + IconSlideAction( + caption: 'Delete', + color: Colors.red, + icon: Icons.delete, + //onTap: () => removeLocation(location), + ), + ], + child: ListTile( + // onTap: () { + // Navigator.pushNamed(context, Routes.closeUp); + // }, + + leading: SizedBox( + width: 64.0, + height: 64.0, + child: ClipRRect( + borderRadius: BorderRadius.circular(64.0), + child: RepaintBoundary( + child: Image( + image: NetworkImage(snapshot.data.url), + ), + ), + ), + ), + ), + ); + } + return CircularProgressIndicator(); + }, + ); + }, + ), + ); // This trailing comma makes auto-formatting nicer for build methods. + } +} diff --git a/lib/src/widgets/slidable.dart b/lib/src/widgets/slidable.dart index 6642438c..ab8d776a 100644 --- a/lib/src/widgets/slidable.dart +++ b/lib/src/widgets/slidable.dart @@ -590,11 +590,45 @@ class SlidableDrawerDelegate extends SlidableStackDelegate { /// A controller that keep tracks of the active [SlidableState] and close /// the previous one. class SlidableController { + SlidableController({ + this.onSlideAnimationChanged, + this.onSlideIsOpenChanged, + }); + + final ValueChanged> onSlideAnimationChanged; + final ValueChanged onSlideIsOpenChanged; + bool _isSlideOpen; + + Animation _slideAnimation; + SlidableState _activeState; SlidableState get activeState => _activeState; set activeState(SlidableState value) { - _activeState?.close(); + _activeState?._flingAnimationControllers(); + _activeState = value; + if (onSlideAnimationChanged != null) { + _slideAnimation?.removeListener(_handleSlideIsOpenChanged); + if (onSlideIsOpenChanged != null) { + _slideAnimation = value?.overallMoveAnimation; + _slideAnimation?.addListener(_handleSlideIsOpenChanged); + if (_slideAnimation == null) { + _isSlideOpen = false; + onSlideIsOpenChanged(_isSlideOpen); + } + } + onSlideAnimationChanged(value?.overallMoveAnimation); + } + } + + void _handleSlideIsOpenChanged() { + if (onSlideIsOpenChanged != null && _slideAnimation != null) { + final bool isOpen = _slideAnimation.value != 0.0; + if (isOpen != _isSlideOpen) { + _isSlideOpen = isOpen; + onSlideIsOpenChanged(_isSlideOpen); + } + } } } @@ -903,6 +937,11 @@ class SlidableState extends State } void close() { + _flingAnimationControllers(); + widget.controller?.activeState = null; + } + + void _flingAnimationControllers() { if (!_dismissing) { _actionsMoveController.fling(velocity: -1.0); _overallMoveController.fling(velocity: -1.0); @@ -1021,6 +1060,7 @@ class SlidableState extends State } void _handleDismiss() { + widget.controller?.activeState = null; final SlideToDismissDelegate slideToDismissDelegate = widget.slideToDismissDelegate; if (slideToDismissDelegate.onDismissed != null) { diff --git a/pubspec.yaml b/pubspec.yaml index 1c1abf8e..1e76db8f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_slidable description: A Flutter implementation of slidable list item with directional slide actions that can be dismissed. -version: 0.4.7 +version: 0.4.8 author: Romain Rastel homepage: https://github.com/letsar/flutter_slidable