-
-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Product Page section expanding animation #267
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,101 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:flutter/cupertino.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:provider/provider.dart'; | ||
import 'package:smooth_ui_library/widgets/models/single_boolean_model.dart'; | ||
import 'package:smooth_ui_library/widgets/smooth_card.dart'; | ||
|
||
class SmoothExpandableCard extends StatelessWidget { | ||
class SmoothExpandableCard extends StatefulWidget { | ||
const SmoothExpandableCard({ | ||
@required this.collapsedHeader, | ||
this.expandedHeader, | ||
@required this.content, | ||
this.expandedHeader, | ||
this.background, | ||
}); | ||
|
||
final Widget collapsedHeader; | ||
final Widget expandedHeader; | ||
final Color background; | ||
final Widget content; | ||
@override | ||
_SmoothExpandableCardState createState() => _SmoothExpandableCardState(); | ||
} | ||
|
||
class _SmoothExpandableCardState extends State<SmoothExpandableCard> | ||
with SingleTickerProviderStateMixin { | ||
bool collapsed = true; | ||
AnimationController _controller; | ||
Animation<double> animation; | ||
static const Duration _ANIMATION_DURATION = Duration(milliseconds: 160); | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
_controller = | ||
AnimationController(vsync: this, duration: _ANIMATION_DURATION); | ||
animation = Tween<double>(begin: 0, end: pi).animate(_controller); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_controller.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return ChangeNotifierProvider<SingleBooleanModel>( | ||
create: (BuildContext context) => SingleBooleanModel(), | ||
child: Consumer<SingleBooleanModel>( | ||
builder: (BuildContext context, SingleBooleanModel singleBooleanModel, | ||
Widget child) { | ||
return AnimatedCrossFade( | ||
duration: const Duration(milliseconds: 160), | ||
firstCurve: Curves.easeInOutBack, | ||
secondCurve: Curves.easeInOutBack, | ||
firstChild: _buildExpandedWidget( | ||
singleBooleanModel, Theme.of(context), true), | ||
secondChild: _buildExpandedWidget( | ||
singleBooleanModel, Theme.of(context), false), | ||
crossFadeState: singleBooleanModel.isActive | ||
? CrossFadeState.showSecond | ||
: CrossFadeState.showFirst, | ||
); | ||
}, | ||
), | ||
return AnimatedCrossFade( | ||
duration: _ANIMATION_DURATION, | ||
crossFadeState: CrossFadeState.showFirst, | ||
firstChild: _buildCard(), | ||
secondChild: _buildCard(), | ||
); | ||
} | ||
|
||
Widget _buildExpandedWidget( | ||
final SingleBooleanModel singleBooleanModel, | ||
final ThemeData themeData, | ||
final bool collapsed, | ||
) { | ||
Widget _buildCard() { | ||
return GestureDetector( | ||
onTap: () => collapsed | ||
? singleBooleanModel.setActive() | ||
: singleBooleanModel.setInactive(), | ||
child: SmoothCard( | ||
collapsed: collapsed, | ||
content: content, | ||
header: collapsed == true ? collapsedHeader : expandedHeader, | ||
onTap: () { | ||
setState(() { | ||
collapsed = !collapsed; | ||
animation.value == 0 ? _controller.forward() : _controller.reverse(); | ||
}); | ||
}, | ||
child: Padding( | ||
padding: const EdgeInsets.only( | ||
right: 8.0, left: 8.0, top: 4.0, bottom: 20.0), | ||
child: Material( | ||
elevation: 8.0, | ||
shadowColor: Colors.black45, | ||
borderRadius: const BorderRadius.all(Radius.circular(10.0)), | ||
color: widget.background ?? Theme.of(context).colorScheme.surface, | ||
child: Container( | ||
padding: const EdgeInsets.all(12.0), | ||
child: Column( | ||
children: <Widget>[ | ||
Row( | ||
mainAxisSize: MainAxisSize.max, | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: <Widget>[ | ||
Container( | ||
child: collapsed | ||
? widget.collapsedHeader | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious: could we just use one header that works for the collapse and expanded states? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @monsieurtanuki the expanded header is now only optional, great idea |
||
: widget.expandedHeader), | ||
AnimatedBuilder( | ||
animation: animation, | ||
child: const Icon(Icons.keyboard_arrow_down), | ||
builder: (BuildContext context, Widget child) { | ||
return Transform.rotate( | ||
angle: animation.value, | ||
child: child, | ||
); | ||
}, | ||
), | ||
], | ||
), | ||
if (collapsed != true) widget.content, | ||
], | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@M123-dev : I think this is causing #270 . header should be required, or we should test if it is null. The AttributeListExpandable does not set it.