Related
Here i have a simple animated dialog which with that i can show one screen into that. this screen into animated dialog sending request on appearing into dialog and can be receive data from server without any problem. but when i drag this dialog on top of screen to get full width and height i get this error:
This UncontrolledProviderScope widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
in fact i think this screen send multiple request when appear into dialog and when its shows as a new screen instance
problem is
ref.read(profileProvider.notifier).send(
method: HTTP.POST,
endPoint: Server.$userProfile,
parameters: {
'uuid': '123',
},
);
into MyProfile class
Full source code:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:my_app/core/service/server.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'core/service/repository/request_repository.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ProviderScope(child: MyApp()),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: MyHome(),
);
}
}
class MyHome extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(
onLongPress: () {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: "hi",
// barrierColor: Colors.black.withOpacity(0.2),
// transitionDuration: Duration(milliseconds: 500),
pageBuilder: (context, pAnim, sAnim) {
return const SafeArea(
child: FloatingDialog(
previewType: PreviewDialogsType.profile,
));
},
transitionBuilder: (context, pAnim, sAnim, child) {
if (pAnim.status == AnimationStatus.reverse) {
return FadeTransition(
opacity: Tween(begin: 0.0, end: 0.0).animate(pAnim),
child: child,
);
} else {
return FadeTransition(
opacity: pAnim,
child: child,
);
}
},
);
},
child: Container(
width: double.infinity,
height: 50.0,
color: Colors.indigoAccent,
child: const Text('LongPress please'),
),
),
],
),
);
}
}
enum PreviewDialogsType {
profile,
}
class FloatingDialog extends StatefulWidget {
final PreviewDialogsType previewType;
const FloatingDialog({super.key, required this.previewType});
#override
_FloatingDialogState createState() => _FloatingDialogState();
}
class _FloatingDialogState extends State<FloatingDialog> with TickerProviderStateMixin {
late double _dragStartYPosition;
late double _dialogYOffset;
late AnimationController _returnBackController;
late Animation<double> _dialogAnimation;
#override
void initState() {
super.initState();
_dialogYOffset = 0.0;
_returnBackController = AnimationController(vsync: this, duration: const Duration(milliseconds: 1300))
..addListener(() {
setState(() {
_dialogYOffset = _dialogAnimation.value;
});
});
}
#override
void dispose() {
_returnBackController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Widget myContents = PreviewDialog(
type: widget.previewType,
);
return Padding(
padding: const EdgeInsets.only(
top: 100.0,
bottom: 10.0,
left: 16.0,
right: 16.0,
),
child: Transform.translate(
offset: Offset(0.0, _dialogYOffset),
child: Column(
children: <Widget>[
const Icon(
Icons.keyboard_arrow_up,
color: Colors.white,
size: 30.0,
),
Expanded(
child: GestureDetector(
onVerticalDragStart: (dragStartDetails) {
_dragStartYPosition = dragStartDetails.globalPosition.dy;
},
onVerticalDragUpdate: (dragUpdateDetails) {
setState(() {
_dialogYOffset = (dragUpdateDetails.globalPosition.dy) - _dragStartYPosition;
});
if (_dialogYOffset < -130) {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, pAnim, sAnim) => myContents,
transitionDuration: const Duration(milliseconds: 500),
transitionsBuilder: (context, pAnim, sAnim, child) {
if (pAnim.status == AnimationStatus.forward) {
return ScaleTransition(
scale: Tween(begin: 0.8, end: 1.0)
.animate(CurvedAnimation(parent: pAnim, curve: Curves.elasticOut)),
child: child,
);
} else {
return child;
}
}),
);
}
},
onVerticalDragEnd: (dragEndDetails) {
_dialogAnimation = Tween(begin: _dialogYOffset, end: 0.0)
.animate(CurvedAnimation(parent: _returnBackController, curve: Curves.elasticOut));
_returnBackController.forward(from: _dialogYOffset);
_returnBackController.forward(from: 0.0);
},
child: myContents,
),
),
],
),
),
);
}
}
class PreviewDialog extends StatefulWidget {
final PreviewDialogsType type;
final String? uuid;
const PreviewDialog({Key? key, required this.type, this.uuid}) : super(key: key);
#override
State<PreviewDialog> createState() => _PreviewDialogState();
}
class _PreviewDialogState extends State<PreviewDialog> {
late ScrollController scrollController;
#override
void initState() {
super.initState();
scrollController = ScrollController();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Scaffold(
body: MyProfile(),
),
),
);
}
}
class MyProfile extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
useEffect(() {
ref.read(profileProvider.notifier).send(
method: HTTP.POST,
endPoint: Server.$userProfile,
parameters: {
'uuid': '123',
},
);
return () {};
}, []);
return SafeArea(
child: Scaffold(
body: Container(
width: double.infinity,
height:double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ref.watch(profileProvider).when(
idle: () => const SizedBox(),
loading: () => const CircularProgressIndicator(),
success: (success) {
return const Text('successful');
},
error: (error, stackTrace) {
return Text('error');
},
)
],
),
),
),
);
}
}
I am making an api call to get some data and then I want to animate in (from the bottom) the widget that displays it. The code for each piece works separately but I can't seem to figure out how to execute the animation after the FutureBuilder has returned from its builder function? Where or how do I call cardController.forward()?
Widget mapCard(Area area) {
return Align(
alignment: Alignment.bottomCenter,
child: AnimatedBuilder(
builder: (BuildContext context, Widget child) {
return InkWell(
onTap: () {
Navigator.of(context).push(_createHeroRoute(area));
},
child: Container(
margin: cardMargin.value,
width: 300,
height: 150,
child: Hero(
tag: 'location',
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 274,
height: 150,
child: Image.network(area.images[0].url,
fit: BoxFit.cover)),
Align(
alignment: Alignment.centerRight,
child: Container(
height: 150,
//30, 42, 65
// 412A1E
decoration: new BoxDecoration(
color: Color.fromARGB(255, 30, 42, 65),
),
child:
Icon(Icons.arrow_right, color: Colors.white))),
],
),
),
),
);
},
animation: cardController,
),
);
}
In the build stack:
FutureBuilder(
future: fArea,
builder: (context, AsyncSnapshot<Area> snap) {
if (snap.hasData) {
return mapCard(snap.data);
} else {
return Text('here');
}
})
You can copy paste run full code below
You can make MapCard a StatefulWidget and call cardController.forward in initState
#override
void initState() {
super.initState();
cardController =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0, end: -300.0).animate(cardController)
..addStatusListener((state) => print('$state'));
cardController.forward();
}
working demo
full code
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MapCard extends StatefulWidget {
Area area;
MapCard(Area data, {Key key}) : super(key: key);
#override
_MapCardState createState() => _MapCardState();
}
class _MapCardState extends State<MapCard> with TickerProviderStateMixin {
AnimationController cardController;
Animation<double> animation;
#override
void initState() {
super.initState();
cardController =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0, end: -300.0).animate(cardController)
..addStatusListener((state) => print('$state'));
cardController.forward();
}
#override
void dispose() {
cardController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: AnimatedBuilder(
animation: cardController,
child: InkWell(
onTap: () {
//Navigator.of(context).push(_createHeroRoute(area));
},
child: Container(
//margin: cardMargin.value,
width: 300,
height: 150,
child: Hero(
tag: 'location',
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 274,
height: 150,
child: Image.network("https://picsum.photos/250?image=9",
fit: BoxFit.cover)),
Align(
alignment: Alignment.centerRight,
child: Container(
height: 150,
//30, 42, 65
// 412A1E
decoration: new BoxDecoration(
color: Color.fromARGB(255, 30, 42, 65),
),
child: Icon(Icons.arrow_right, color: Colors.white))),
],
),
),
),
),
builder: (BuildContext context, Widget child) {
return Transform.translate(
offset: Offset(0, animation.value),
child: child,
);
},
),
);
}
}
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 Area {
String data;
Area(this.data);
}
class _MyHomePageState extends State<MyHomePage> {
Future<Area> fArea;
Future<Area> getArea() async {
await Future.delayed(Duration(seconds: 3), () {});
return Future.value(Area("data"));
}
#override
void initState() {
fArea = getArea();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: fArea,
builder: (context, AsyncSnapshot<Area> snap) {
switch (snap.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snap.hasData) {
return MapCard(snap.data);
} else {
return Center(child: CircularProgressIndicator());
}
}
}));
}
}
How can I create a page that slides from the bottom of the screen and when it passes a certain threshold automatically fills the screen?
Something like this:
Consider using DraggableScrollableSheet class or sliding up panel package
You can use PageRouteBuilder to build your own navigator transitions.
This is the CustomPageRouteBuilder class as you need, and you can change it into other transitons like scale fade rotate, etc.
class CustomPageRouteBuilder<T> extends PageRouteBuilder<T> {
CustomPageRouteBuilder({
this.widget,
})
: assert(widget != null),
super(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return widget;
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final Widget transition = SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: Offset(0.0, -0.7),
).animate(secondaryAnimation),
child: child,
),
);
return transition;
},
);
final Widget widget;
}
And how to use:
Navigator.push(
context,
CustomPageRouteBuilder(
widget: SecondPage(),
),
);
You can check the sample below:
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,
),
home: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
const FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.red,
child: Center(
child: Text(
'First Page',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.arrow_forward_ios,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
CustomPageRouteBuilder(
widget: SecondPage(),
),
);
},
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: Center(
child: Text(
'Second Page',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.pop(context);
},
),
);
}
}
class CustomPageRouteBuilder<T> extends PageRouteBuilder<T> {
CustomPageRouteBuilder({
this.widget,
})
: assert(widget != null),
super(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return widget;
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final Widget transition = SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: Offset(0.0, -0.7),
).animate(secondaryAnimation),
child: child,
),
);
return transition;
},
);
final Widget widget;
}
I would recommend the PageView widget, you can see more here and view the documentation.
Using the scrollDirection property, you can set it to vertical so that you can swipe up to reveal a new page and you can swipe down to go back.
is any solution to make drag and drop dialogs in flutter? for example after showing dialog in center of screen i would like to drag it to top of screen to make fullscreen dialog over current cover, for example this code is simple implementation to show dialog and i'm not sure, how can i do that
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
}
}
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (_) => FunkyOverlay(),
);
},
icon: Icon(Icons.message),
label: Text("PopUp!")),
),
);
}
}
class FunkyOverlay extends StatefulWidget {
#override
State<StatefulWidget> createState() => FunkyOverlayState();
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Text("Well hello there!"),
),
),
),
),
);
}
}
This is one way to do it ,
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: App(),
));
}
class App extends StatefulWidget {
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.open_in_new),
onPressed: () {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: "hi",
barrierColor: Colors.black.withOpacity(0.2),
transitionDuration: Duration(milliseconds: 500),
pageBuilder: (context, pAnim, sAnim) {
return SafeArea(child: FloatingDialog());
},
transitionBuilder: (context, pAnim, sAnim, child) {
if (pAnim.status == AnimationStatus.reverse) {
return FadeTransition(
opacity: Tween(begin: 0.0, end: 0.0).animate(pAnim),
child: child,
);
} else {
return FadeTransition(
opacity: pAnim,
child: child,
);
}
},
);
},
),
);
}
}
class FloatingDialog extends StatefulWidget {
#override
_FloatingDialogState createState() => _FloatingDialogState();
}
class _FloatingDialogState extends State<FloatingDialog>
with TickerProviderStateMixin {
double _dragStartYPosition;
double _dialogYOffset;
Widget myContents = MyScaffold();
AnimationController _returnBackController;
Animation<double> _dialogAnimation;
#override
void initState() {
super.initState();
_dialogYOffset = 0.0;
_returnBackController =
AnimationController(vsync: this, duration: Duration(milliseconds: 1300))
..addListener(() {
setState(() {
_dialogYOffset = _dialogAnimation.value;
print(_dialogYOffset);
});
});
}
#override
void dispose() {
_returnBackController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
top: 100.0,
bottom: 10.0,
left: 10.0,
right: 10.0,
),
child: Transform.translate(
offset: Offset(0.0, _dialogYOffset),
child: Column(
children: <Widget>[
Icon(
Icons.keyboard_arrow_up,
color: Colors.white,
),
Expanded(
child: GestureDetector(
onVerticalDragStart: (dragStartDetails) {
_dragStartYPosition = dragStartDetails.globalPosition.dy;
print(dragStartDetails.globalPosition);
},
onVerticalDragUpdate: (dragUpdateDetails) {
setState(() {
_dialogYOffset = (dragUpdateDetails.globalPosition.dy) -
_dragStartYPosition;
});
print(_dialogYOffset);
if (_dialogYOffset < -90.0) {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, pAnim, sAnim) => myContents,
transitionDuration: Duration(milliseconds: 500),
transitionsBuilder: (context, pAnim, sAnim, child) {
if (pAnim.status == AnimationStatus.forward) {
return ScaleTransition(
scale: Tween(begin: 0.8, end: 1.0).animate(
CurvedAnimation(
parent: pAnim,
curve: Curves.elasticOut)),
child: child,
);
} else {
return FadeTransition(
opacity: pAnim,
child: child,
);
}
}),
);
}
},
onVerticalDragEnd: (dragEndDetails) {
_dialogAnimation = Tween(begin: _dialogYOffset, end: 0.0)
.animate(CurvedAnimation(
parent: _returnBackController,
curve: Curves.elasticOut));
_returnBackController.forward(from: _dialogYOffset);
_returnBackController.forward(from: 0.0);
},
child: myContents,
),
),
],
),
),
);
}
}
class MyScaffold extends StatelessWidget {
const MyScaffold({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Channels"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(),
body: Placeholder(),
),
),
);
},
),
),
);
}
}
Output:
You can try this.
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _shown = false;
double _topOffset = 20, _dialogHeight = 400;
Duration _duration = Duration(milliseconds: 400);
Offset _offset, _initialOffset;
#override
void didChangeDependencies() {
super.didChangeDependencies();
var size = MediaQuery.of(context).size;
_offset = Offset(size.width, (size.height - _dialogHeight) / 2);
_initialOffset = _offset;
}
#override
Widget build(BuildContext context) {
var appBarColor = Colors.blue[800];
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () => setState(() => _shown = !_shown)),
body: SizedBox.expand(
child: Stack(
children: <Widget>[
Container(
color: appBarColor,
child: SafeArea(
bottom: false,
child: Align(
child: Column(
children: <Widget>[
MyAppBar(
title: "Image",
color: appBarColor,
icon: Icons.home,
onPressed: () {},
),
Expanded(child: Image.asset("assets/images/landscape.jpeg", fit: BoxFit.cover)),
],
),
),
),
),
AnimatedOpacity(
opacity: _shown ? 1 : 0,
duration: _duration,
child: Material(
elevation: 8,
color: Colors.grey[900].withOpacity(0.5),
child: _shown
? GestureDetector(
onTap: () => setState(() => _shown = !_shown),
child: Container(color: Colors.transparent, child: SizedBox.expand()),
)
: SizedBox.shrink(),
),
),
// this shows our dialog
Positioned(
top: _offset.dy,
left: 10,
right: 10,
height: _shown ? null : 0,
child: AnimatedOpacity(
duration: _duration,
opacity: _shown ? 1 : 0,
child: GestureDetector(
onPanUpdate: (details) => setState(() => _offset += details.delta),
onPanEnd: (details) {
// when tap is lifted and current y position is less than set _offset, navigate to the next page
if (_offset.dy < _topOffset) {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, anim1, anim2) => Screen2(),
transitionDuration: _duration,
transitionsBuilder: (context, anim1, anim2, child) {
bool isForward = anim1.status == AnimationStatus.forward;
Tween<double> tween = Tween(begin: isForward ? 0.9 : 0.5, end: 1);
return ScaleTransition(
scale: tween.animate(
CurvedAnimation(
parent: anim1,
curve: isForward ? Curves.bounceOut : Curves.easeOut,
),
),
child: child,
);
},
),
).then((_) {
_offset = _initialOffset;
});
}
// make the dialog come back to the original position
else {
Timer.periodic(Duration(milliseconds: 5), (timer) {
if (_offset.dy < _initialOffset.dy - _topOffset) {
_offset = Offset(_offset.dx, _offset.dy + 15);
setState(() {});
} else if (_offset.dy > _initialOffset.dy + _topOffset) {
_offset = Offset(_offset.dx, _offset.dy - 15);
setState(() {});
} else
timer.cancel();
});
}
},
child: Column(
children: <Widget>[
Icon(Icons.keyboard_arrow_up, color: Colors.white, size: 32),
Hero(
tag: "MyTag",
child: SizedBox(
height: _dialogHeight, // makes sure we don't exceed than our specified height
child: SingleChildScrollView(child: CommonWidget(appBar: MyAppBar(title: "FlutterLogo", color: Colors.orange))),
),
),
],
),
),
),
),
],
),
),
);
}
}
// this app bar is used in 1st and 2nd screen
class MyAppBar extends StatelessWidget {
final String title;
final Color color;
final IconData icon;
final VoidCallback onPressed;
const MyAppBar({Key key, #required this.title, #required this.color, this.icon, this.onPressed}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: kToolbarHeight,
color: color,
width: double.maxFinite,
alignment: Alignment.centerLeft,
child: Row(
children: <Widget>[
icon != null ? IconButton(icon: Icon(icon), onPressed: onPressed, color: Colors.white,) : SizedBox(width: 16),
Text(
title,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
);
}
}
// this is the one which is shown in both Dialog and Screen2
class CommonWidget extends StatelessWidget {
final bool isFullscreen;
final Widget appBar;
const CommonWidget({Key key, this.isFullscreen = false, this.appBar}) : super(key: key);
#override
Widget build(BuildContext context) {
var child = Container(
width: double.maxFinite,
color: Colors.blue,
child: FlutterLogo(size: 300, colors: Colors.orange),
);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
appBar,
isFullscreen ? Expanded(child: child) : child,
],
);
}
}
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
var appBarColor = Colors.orange;
return Scaffold(
body: Container(
color: appBarColor,
child: SafeArea(
bottom: false,
child: CommonWidget(
isFullscreen: true,
appBar: MyAppBar(
title: "FlutterLogo",
color: appBarColor,
icon: Icons.arrow_back,
onPressed: () => Navigator.pop(context),
),
),
),
),
);
}
}
The native navigation transition for iOS is RightToLeft. The Native Navigation transition for ANDROID is Bottom to Top. I would like to override the Native Navigation transition in Flutter to have the same RIght to Left transtion across iOS and ANDROID. To do this, I am trying to use PageRouteBuilder but no luck getting it to work. The first code block, I have a very basic screen that works... but natively. The second snippet includes the navigation transition code I am attempting to integrate.
The Code I am trying to fix.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//==============================================
// How would I force a horizontal transition?
mbNav001(context),
new Text(
'Screen 1',
),
],
),
),
);
}
}
//===================================================
Padding mbNav001(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(28.0),
child: new MaterialButton(
height: 80.0,
padding: EdgeInsets.all(50.0),
minWidth: double.infinity,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
child: new Text(
"material button a",
style: new TextStyle(
fontSize: 20.0,
color: Colors.yellow,
),
),
splashColor: Colors.redAccent,
// ----- This line is giving me error...
onPressed: () {
print('click mb');
//===========================================================
// How to force a horizontal trans in Navigation?
//===========================================================
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
}
// expecting to find... :
),
);
}
//===================================================
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first screen when tapped!
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
this is the transition code I am trying to add.
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(secondaryAnimation),
child: child,
),
);
},
);
Navigator.of(context).push(pageRoute);
Using CupertinoPageRoute instead of MaterialPageRoute resolved the same issue in my case, without trying to apply any custom animation.
Navigator.of(context).push(CupertinoPageRoute(builder: (context) => MyPanel()));
This is the code i've used to achieve the slide up from the bottom animation on iOS. You'd just need to edit the tween values to achieve a left to right animation.
var builder = PageRouteBuilder(
pageBuilder: (BuildContext context,
Animation animation,
Animation secondaryAnimation) {
return YourChildWidgetHere();
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return new SlideTransition(
position: new Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: child,
);
});
Navigator.of(context).push(builder);