Related
I'm trying to achieve the Hero & shake upright animation result attached gif here.
This is the result I got so far.
Seems like the Hero widget conflicts with the animation I've applied. It seems to be working for the 200 & 300 card. But when 100 is tapped, it seems to be working differently. Attached below are the code for the demo above.
Tried using WidgetsBinding.instance.addPostFrameCallback and SchedulerBinding.instance.addPersistentFrameCallback.
Is there any way to get the expected result instead of using the code I've used?
dummy_data.dart
class _DummyData {
final IconData icons;
final Color colors;
final String backText;
const _DummyData(this.icons, this.colors, this.backText);
}
const List<_DummyData> _datas = [
_DummyData(Icons.abc, Colors.blue, '100'),
_DummyData(Icons.alarm, Colors.red, '200'),
_DummyData(Icons.shop, Colors.green, '300'),
];
playground_list.dart
class PlayGroundList extends StatelessWidget {
const PlayGroundList({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: CustomScrollView(
slivers: [
const SliverToBoxAdapter(child: SizedBox(height: 50)),
SliverList(
delegate: SliverChildBuilderDelegate(
childCount: _datas.length,
(context, index) => PlayGroundCardWidget(dummy: _datas[index]),
),
),
],
),
);
}
}
playground_card_widget.dart
class PlayGroundCardWidget extends StatelessWidget {
final _DummyData dummy;
const PlayGroundCardWidget({super.key, required this.dummy});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 5.w, vertical: 1.5.h),
height: 350,
child: GestureDetector(
onTap: () => Navigator.push(
context,
PageRouteBuilder(
transitionsBuilder: (context, animation, _, child) =>
FadeTransition(
opacity: Tween(
begin: 0.0,
end: 1.0,
).chain(CurveTween(curve: Curves.ease)).animate(animation),
child: child,
),
pageBuilder: (context, _, __) => PlayGroundDetail(dummy: dummy),
),
),
child: Stack(
alignment: Alignment.center,
children: [
Positioned.fill(
child: Hero(
tag: dummy.colors.value,
child: Material(color: dummy.colors),
),
),
Align(
alignment: Alignment.topCenter,
child: Hero(
tag: dummy.backText,
child: Material(
color: Colors.transparent,
child: Text(
dummy.backText,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 140.0,
),
),
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 3.h),
Expanded(
flex: 12,
child: Hero(
tag: dummy.icons,
child: Icon(dummy.icons, size: 60.0),
),
),
],
),
],
),
),
);
}
}
playground_detail.dart
const _shakeDuration = Duration(milliseconds: 900);
class PlayGroundDetail extends StatefulWidget {
final _DummyData dummy;
const PlayGroundDetail({super.key, required this.dummy});
#override
State<PlayGroundDetail> createState() => _PlayGroundDetailState();
}
class _PlayGroundDetailState extends State<PlayGroundDetail>
with TickerProviderStateMixin {
late final PageController _pageController;
#override
void initState() {
super.initState();
_pageController = PageController();
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
CustomScrollView(
slivers: [
const SliverToBoxAdapter(child: SizedBox(height: 50)),
SliverToBoxAdapter(
child: SizedBox(
height: 350,
child: Stack(
children: [
Positioned.fill(
child: Hero(
tag: widget.dummy.colors.value,
child: Material(color: widget.dummy.colors),
),
),
Align(
alignment: Alignment.topCenter,
child: Hero(
tag: widget.dummy.backText,
child: Material(
color: Colors.transparent,
child: ShakeTransitionWidget(
axis: Axis.vertical,
duration: _shakeDuration,
offset: 30,
child: Text(
widget.dummy.backText,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 140.0,
),
),
),
),
),
),
Center(
child: Hero(
tag: widget.dummy.icons,
child: ShakeTransitionWidget(
axis: Axis.vertical,
offset: 5,
duration: _shakeDuration,
child: Icon(widget.dummy.icons, size: 60.0),
),
),
),
],
),
),
),
],
),
],
),
);
}
}
shake_transition_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class ShakeTransitionWidget extends StatefulWidget {
final Widget child;
final Duration duration;
final double offset;
final Axis axis;
const ShakeTransitionWidget({
super.key,
required this.child,
required this.duration,
required this.offset,
required this.axis,
});
#override
State<ShakeTransitionWidget> createState() => _ShakeTransitionWidgetState();
}
class _ShakeTransitionWidgetState extends State<ShakeTransitionWidget>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation _animation;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.duration,
);
_animation = Tween(
begin: 1.0,
end: 0.0,
).chain(CurveTween(curve: Curves.elasticOut)).animate(_controller);
// Tried this.
// WidgetsBinding.instance.addPostFrameCallback((_) => _controller.forward());
if (SchedulerBinding.instance.schedulerPhase ==
SchedulerPhase.persistentCallbacks) {
_controller.forward();
}
SchedulerBinding.instance.addPersistentFrameCallback((timeStamp) {});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (ctx, _) => Transform.translate(
offset: widget.axis == Axis.horizontal
? Offset(_animation.value * widget.offset, 0.0)
: Offset(0.0, _animation.value * widget.offset),
child: widget.child,
),
);
}
}
Wrap your Hero widget with ShakeTransitionWidget instead of the other way around:
// ... Hero is the child, not the parent
ShakeTransitionWidget(
axis: Axis.vertical,
duration: _shakeDuration,
offset: 30,
child: Hero(
tag: widget.dummy.backText,
// ...
Now inside the initState() of _ShakeTransitionWidgetState simply call _controller.forward() without any SchedulerBinding or WidgetsBinding.
As a rule of thumb, usually the Hero and its children should not change from page to page. Instead add modifications to the parent widgets.
See gif.
Here's a screenshot of a simple flutter app:
Pushing the ++/-- buttons adds/removes a panel to/from a Container. The container is outlined with a blue shadow.
Now, as soon as the container grows too close to the bottom border, it's shadow gets clipped on the left and right border.
Do you have any ideas what causes the clipping and how to avoid it?
This is how the code looks:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
ScrollController _scrollController;
#override
void initState() {
_scrollController = ScrollController();
super.initState();
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
if (_counter > 0) {
_counter--;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20),
child: ListView(
controller: _scrollController,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 30),
_getButtonPanel(),
SizedBox(height: 20),
Container(
padding: EdgeInsets.all(20),
child: _getChildren(_counter),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.blue,
offset: Offset(0, 0),
blurRadius: 10.0,
spreadRadius: 5.0,
)
],
),
),
SizedBox(height: 20),
],
),
],
),
),
);
}
Widget _getButtonPanel() {
return Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
OutlineButton(
onPressed: _decrementCounter,
child: Text('--'),
),
Spacer(),
OutlineButton(
onPressed: _incrementCounter,
child: Text('++'),
),
],
);
}
Widget _getChildren(int cnt) {
List<Widget> children = [];
for (int i = 0; i < cnt; i++) {
children.add(_getItem(1 + i));
}
return Column(
children: children,
);
}
Widget _getItem(int i) {
return Column(
children: <Widget>[
SizedBox(
height: 50,
),
Text('Child panel $i'),
SizedBox(
height: 50,
),
],
);
}
}
You should add margin to the container containing the list to prevent it from clipping.
Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(15), //Add margin
child: _getChildren(_counter),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.blue,
offset: Offset(0, 0),
blurRadius: 10.0,
spreadRadius: 5.0,
)
],
),
);
I am a flutter newbie. I got the code for an animated Flutter login page from GitHub and I was modifying it to suit my requirements. I copied the code for webview from another app I made where it was working, but when I click the button in my new app, nothing happens.
Here is the modified code:
https://github.com/mansto0/Flutter-Login/blob/master/main.dart
You can copy paste run full code below
You can move MaterialApp to main()
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()));
}
working demo
full code
import 'package:avatar_glow/avatar_glow.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'package:webview_flutter/webview_flutter.dart';
class DelayedAnimation extends StatefulWidget {
final Widget child;
final int delay;
DelayedAnimation({#required this.child, this.delay});
#override
_DelayedAnimationState createState() => _DelayedAnimationState();
}
class _DelayedAnimationState extends State<DelayedAnimation>
with TickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _animOffset;
#override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 800));
final curve =
CurvedAnimation(curve: Curves.decelerate, parent: _controller);
_animOffset =
Tween<Offset>(begin: const Offset(0.0, 0.35), end: Offset.zero)
.animate(curve);
if (widget.delay == null) {
_controller.forward();
} else {
Timer(Duration(milliseconds: widget.delay), () {
_controller.forward();
});
}
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
child: SlideTransition(
position: _animOffset,
child: widget.child,
),
opacity: _controller,
);
}
}
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()));
}
class MyWebView extends StatelessWidget {
final String title;
final String selectedUrl;
final Completer<WebViewController> _controller =
Completer<WebViewController>();
MyWebView({
#required this.title,
#required this.selectedUrl,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: WebView(
initialUrl: selectedUrl,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
));
}
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
final int delayedAmount = 500;
double _scale;
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(
milliseconds: 200,
),
lowerBound: 0.0,
upperBound: 0.1,
)..addListener(() {
setState(() {});
});
super.initState();
}
#override
Widget build(BuildContext context) {
final color = Colors.white;
_scale = 1 - _controller.value;
return Scaffold(
backgroundColor: Color(0xFF1E88E5),
body: Center(
child: Column(
children: <Widget>[
AvatarGlow(
endRadius: 90,
duration: Duration(seconds: 2),
glowColor: Colors.white24,
repeat: true,
repeatPauseDuration: Duration(seconds: 2),
startDelay: Duration(seconds: 1),
child: Material(
elevation: 8.0,
shape: CircleBorder(),
child: CircleAvatar(
backgroundColor: Colors.grey[100],
child: FlutterLogo(
size: 50.0,
),
radius: 50.0,
)),
),
DelayedAnimation(
child: Text(
"Hi There",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25.0,
color: color),
),
delay: delayedAmount + 1000,
),
DelayedAnimation(
child: Text(
"Welcome to DocSpot",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: color),
),
delay: delayedAmount + 2000,
),
SizedBox(
height: 30.0,
),
DelayedAnimation(
child: Text(
"Your New Personal",
style: TextStyle(fontSize: 15.0, color: color),
),
delay: delayedAmount + 3000,
),
DelayedAnimation(
child: Text(
"Healthcare Companion",
style: TextStyle(fontSize: 15.0, color: color),
),
delay: delayedAmount + 3000,
),
SizedBox(
height: 100.0,
),
DelayedAnimation(
child: GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
child: Transform.scale(
scale: _scale,
child: _animatedButtonUI,
),
),
delay: delayedAmount + 4000,
),
SizedBox(
height: 50.0,
),
DelayedAnimation(
child: new MaterialButton(
height: 60.0,
minWidth: 270.0,
color: Colors.white,
textColor: Color(0xFF1E88E5),
child: new Text("Register"),
onPressed: () => {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => MyWebView(
title: "DocHelp",
selectedUrl: "https://flutter.dev/",
))),
},
splashColor: Colors.blueAccent[300],
),
delay: delayedAmount + 5000,
),
],
),
)
// Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: <Widget>[
// Text('Tap on the Below Button',style: TextStyle(color: Colors.grey[400],fontSize: 20.0),),
// SizedBox(
// height: 20.0,
// ),
// Center(
// ),
// ],
// ),
);
}
Widget get _animatedButtonUI => Container(
height: 60,
width: 270,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Colors.white,
),
child: Center(
child: Text(
'Login',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Color(0xFF1E88E5),
),
),
),
);
void _onTapDown(TapDownDetails details) {
_controller.forward();
}
void _onTapUp(TapUpDetails details) {
_controller.reverse();
}
}
I created a tab Widget which one can press one tab and it highlights and animates, but after pressing another tab the previews tab should animate back to it's previews state.
use the blow code to run on your IDE.
import 'package:flutter/material.dart';
class AnimatedTabsOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: 300.0,
height: 60.0,
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 3.5),
// spreadRadius: 0.0,
blurRadius: 4.0,
),
]),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
BuildTab(),
BuildTab(),
BuildTab(),
],
),
);
}
}
class BuildTab extends StatefulWidget {
#override
_BuildTabState createState() => _BuildTabState();
}
class _BuildTabState extends State<BuildTab>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
bool isVisible = false;
bool setActiveBackgroundColor = false;
animateNow() {
controller =
AnimationController(duration: Duration(milliseconds: 200), vsync: this);
controller.forward();
animation = Tween<double>(begin: 50, end: 150).animate(controller)
..addListener(() => setState(() {
setActiveBackgroundColor = true;
isVisible = true;
}));
// ..addStatusListener((AnimationStatus status) {
// if (status == AnimationStatus.completed) {
// controller.reverse();
// setState(() {
// setActiveBackgroundColor = false;
// isVisible = false;
// });
// }
// });
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
print(
"setActiveBackgroundColor $setActiveBackgroundColor, isVisible: $isVisible");
return InkWell(
key: UniqueKey(),
onTap: () => animateNow(),
child: Container(
width: animation?.value ?? 50,
height: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: setActiveBackgroundColor == true ? Colors.blue : Colors.white,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.home),
Flexible(
child: Visibility(visible: isVisible, child: Text("Home")),
)
],
),
),
);
}
}
I tried to use global keys, and call the callback function in another way but it did not do the job.
Better use AnimatedContainer. I simplified your code. There are multiple solutions, everything depends on you but if you just that functionality, this is a nice pattern with minimum rebuild trigger:
class AnimatedTabsOne extends StatefulWidget {
#override
_AnimatedTabsOneState createState() => _AnimatedTabsOneState();
}
class _AnimatedTabsOneState extends State<AnimatedTabsOne> {
int index;
final myTabs = [BuildTab(), BuildTab(), BuildTab()];
#override
Widget build(BuildContext context) {
return Container(
width: 300.0,
height: 60.0,
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 3.5),
// spreadRadius: 0.0,
blurRadius: 4.0,
),
]),
child: Row(
children: List<Widget>.from(
myTabs.map((tab) {
final index = myTabs.indexOf(tab);
return GestureDetector(
child: BuildTab(isVisible: this.index == index),
onTap: () {
if (this.index != index) {
setState(() {
this.index = index;
});
}
},
);
}),
),
),
);
}
}
class BuildTab extends StatelessWidget {
final bool isVisible;
BuildTab({this.isVisible = false});
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 200),
width: isVisible ? 150 : 50,
height: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: isVisible ? Colors.blue : Colors.white,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.home),
Flexible(
child: Visibility(visible: isVisible, child: Text("Home")),
)
],
),
);
}
}
By this simple code I can show dialog on bottom of screen like with this screenshot:
But I have three simple issue:
set margin on bottom of dialog such as 20.0 on showing dialog
using controller.reverse() on dismiss dialog
dismiss dialog on click on outside of dialog
Full source code:
import 'package:flutter/material.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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void showPopup() {
showDialog(
context: context,
builder: (_) => PopUp(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: showPopup,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class PopUp extends StatefulWidget {
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> opacityAnimation;
Tween<double> opacityTween = Tween<double>(begin: 0.0, end: 1.0);
Tween<double> marginTopTween = Tween<double>(begin: 300, end: 280);
Animation<double> marginTopAnimation;
#override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
marginTopAnimation = marginTopTween.animate(controller)
..addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: opacityTween.animate(controller),
child: Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.only(
top: marginTopAnimation.value,
left:20.0,
right:20.0,
),
color: Colors.red,
child: Text("Container"),
),
),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
Screenshot:
Code:
floatingActionButton: FloatingActionButton(
onPressed: () {
showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: Duration(milliseconds: 700),
context: context,
pageBuilder: (context, anim1, anim2) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 300,
child: SizedBox.expand(child: FlutterLogo()),
margin: EdgeInsets.only(bottom: 50, left: 12, right: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
),
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween(begin: Offset(0, 1), end: Offset(0, 0)).animate(anim1),
child: child,
);
},
);
},
)
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: CityPage()));
}
class CityPage extends StatelessWidget {
const CityPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: TextButton(
child: const Text('Press me'),
onPressed: () => BottomDialog().showBottomDialog(context),
),
),
],
),
);
}
}
class BottomDialog {
void showBottomDialog(BuildContext context) {
showGeneralDialog(
barrierLabel: "showGeneralDialog",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.6),
transitionDuration: const Duration(milliseconds: 400),
context: context,
pageBuilder: (context, _, __) {
return Align(
alignment: Alignment.bottomCenter,
child: _buildDialogContent(),
);
},
transitionBuilder: (_, animation1, __, child) {
return SlideTransition(
position: Tween(
begin: const Offset(0, 1),
end: const Offset(0, 0),
).animate(animation1),
child: child,
);
},
);
}
Widget _buildDialogContent() {
return IntrinsicHeight(
child: Container(
width: double.maxFinite,
clipBehavior: Clip.antiAlias,
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Material(
child: Column(
children: [
const SizedBox(height: 16),
_buildImage(),
const SizedBox(height: 8),
_buildContinueText(),
const SizedBox(height: 16),
_buildEmapleText(),
const SizedBox(height: 16),
_buildTextField(),
const SizedBox(height: 16),
_buildContinueButton(),
],
),
),
),
);
}
Widget _buildImage() {
const image =
'https://user-images.githubusercontent.com/47568606/134579553-da578a80-b842-4ab9-ab0b-41f945fbc2a7.png';
return SizedBox(
height: 88,
child: Image.network(image, fit: BoxFit.cover),
);
}
Widget _buildContinueText() {
return const Text(
'Continue with account',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w500,
),
);
}
Widget _buildEmapleText() {
return const Text(
'example.com',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
);
}
Widget _buildTextField() {
const iconSize = 40.0;
return Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1, color: Colors.grey.withOpacity(0.4)),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
child: Row(
children: [
Container(
width: iconSize,
height: iconSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[200],
),
child: const Center(
child: Text('Е'),
),
),
const SizedBox(height: 16),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'elisa.g.beckett#gmail.com',
style: TextStyle(
fontWeight: FontWeight.w600,
),
),
Text('**********'),
],
)
],
),
);
}
Widget _buildContinueButton() {
return Container(
height: 40,
width: double.maxFinite,
decoration: const BoxDecoration(
color: Color(0xFF3375e0),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: RawMaterialButton(
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
child: const Center(
child: Text(
'Continue',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
),
);
}
}
From top to bottom you can use
bool _fromTop = true;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: Duration(milliseconds: 700),
context: context,
pageBuilder: (context, anim1, anim2) {
return Align(
alignment: _fromTop ? Alignment.topCenter : Alignment.bottomCenter,
child: Container(
height: 300,
child: SizedBox.expand(child: FlutterLogo()),
margin: EdgeInsets.only(top: 50, left: 12, right: 12, bottom: 50),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
),
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween(begin: Offset(0, _fromTop ? -1 : 1), end: Offset(0, 0)).animate(anim1),
child: child,
);
},
);
},
),
);
}
Output:
Not sure if I got your question clearly, if this is what you are looking for, replace your PopUp class with mine.
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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void showPopup() {
showDialog(
context: context,
builder: (_) => PopUp(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: showPopup,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class PopUp extends StatefulWidget {
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> with TickerProviderStateMixin {
AnimationController controller;
double _bottom = 0, _fromTop = 300, _screenHeight, _containerHeight = 300;
#override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this)
..addListener(() {
Timer.periodic(Duration(milliseconds: 15), (timer) {
if (_bottom < _screenHeight - _fromTop - _containerHeight) {
_bottom += 1;
setState(() {});
}
});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
_screenHeight = MediaQuery.of(context).size.height;
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Stack(
children: <Widget>[
Positioned(
bottom: _bottom,
left: 0,
right: 0,
child: Container(height: _containerHeight, color: Colors.green),
),
],
),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
I think showModalBottomSheet function does this out of the box.