How to transition to a new screen instantly - flutter

I want to create a transition immediately from one screen to another (no animation). However isInitialRoute generates an error due to an update to the Router widget. The suggestion is to use onGenerateInitialRoutes but I am not sure how to implement it.
The flutter design doc recommends this:
Routes & RouteSettings
Currently, the only way to add a Route to the history stack without playing its entrance animation is to mark it as an initial route in its RouteSettings. The declarative API requires that routes can be added without animation at any time. This is useful when the route is covered by another route and playing the animation simply doesn't make sense. To support this, a didAdd method is added to the Route interface. This method is called by the Navigator instead of didPush when the Route should just be added without its regular entrance animation. To simplify things, this new method will also be used to bring the initial route on screen. This makes the RouteSettings.initialRoute parameter useless and it will be removed from RouteSettings. This is a minor breaking change.
This is the code that is generating the error on isInitialRoute:
import 'package:flutter/cupertino.dart'
show
CupertinoApp,
CupertinoButton,
CupertinoPageRoute,
CupertinoPageScaffold;
import 'package:flutter/widgets.dart'
show
BuildContext,
Center,
Column,
Navigator,
Route,
RouteSettings,
SafeArea,
Spacer,
Text,
runApp,
Widget;
Widget makeButton(BuildContext context, String routeName) =>
new CupertinoButton(
onPressed: () => Navigator.pushReplacementNamed(context, routeName),
child: Text('Go to \'$routeName\''),
);
Route generateRoute(RouteSettings settings) {
switch (settings.name) {
case 'not-animated':
return new CupertinoPageRoute(
settings: RouteSettings(name: settings.name, isInitialRoute: true),
builder: (context) => CupertinoPageScaffold(
child: SafeArea(
child: Center(
child: Column(
children: [
Spacer(),
Text('This is \'not-animated\''),
makeButton(context, 'animated'),
Spacer(),
],
),
),
),
),
);
default:
return null;
}
}
void main() {
runApp(
CupertinoApp(
onGenerateRoute: generateRoute,
initialRoute: 'animated',
routes: {
'animated': (context) => CupertinoPageScaffold(
child: SafeArea(
child: Center(
child: Column(
children: [
Spacer(),
Text('This is \'animated\''),
makeButton(context, 'not-animated'),
Spacer(),
],
),
),
),
),
},
),
);
}

If You Want to just navigate your screen add this to your code
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => yoursecondscreenname()),
);
},

Related

Flutter showDialog

Can i showDialog inside a function without passing context?
void test(){
showDialog(context: context, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));
}
Sorry i didn't explain very well, without passing context to function, not to showDialog
According to the doc (https://api.flutter.dev/flutter/material/showDialog.html) you can't, it's required.
the short answer is no, you can't.
the long answer:
first, the BuildContext is a object type, so in order to remove conflictions between the context property and the context value we're going to rename it to contextGotFromUI.
Note: contextGotFromUI here is just a BuildContext object sp we can rename it with whatever we want.
just to not get confused by the same names
void test(){
showDialog(context: contextGotFromUI, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));}
the context property in the showDialog is required to set from it's implementation:
Future<T?> showDialog<T>({
required BuildContext context,
required WidgetBuilder builder,
bool barrierDismissible = true,
// more code
the BuildContext is an important topic to understand in flutter, to show a dialog widget on top of the screen the user is actually navigating in and seeing at any time, the BuildContext is what tells to show it on top of the widget with that specific context, and not other screens.
As from the showDialog official documentation:
The context argument is used to look up the Navigator and Theme for the dialog. It is only used when the method is called. Its corresponding widget can be safely removed from the tree before the dialog is closed.
so in order to show a dialog from an external method, you need to pass a context that belongs to a specific widget, then use it in the showDialog:
void test(BuildContext contextGotFromUI){
showDialog(context: contextGotFromUI, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));}
then from your UI where you're calling that method, pass it:
Widget build(BuildContext) {
// widgets
//...
onPressed: () {
test(context); // will show an expected dialog on the screen
}
}
Yes, you can but you have to create the function inside a stateful widget not in the normal classes.
in case you create the function in a normal class the context will be required!
void test(BuildContext context){
showDialog(context: context, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));
}

How to navigate properly in SplitScreen

I am working on the iPad and I have set up the screen to be SplitScreens. Thus I have two Navigators side by side. However, when I push a new Screen from the second Navigator I get a weird animation where the second screen (right) overlaps the first screen (left).
Please look at this video that explains it very good:
https://imgur.com/a/93UmrGm
SplitView(
menu: Navigator(
key: firstSplitNavigatorKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(
builder: (context) =>
const KursplanungScreen(title: 'Demo App'),
);
},
),
content: Navigator(
key: secondSplitNavigatorKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(
builder: (context) => const Scaffold(
body: Center(
child: Text(
"Herzlich Willkommen!"))),
);
},
),
menuWidth: 300,
),
Then somewhere in the second screen i call this:
TextButton(
onPressed: () => secondSplitNavigatorKey.currentState!.push(
MaterialPageRoute(
builder: (_) => const CourseOverviewScreen(),
),
),
child: const Text("Bearbeiten"),
),
How can I avoid that third weird animation?
You need to use a Navigator object to handle the right side pages.Use a Navigator object as widget father of the right side and handle the stack of pages to navigate only in this side. The default Navigator will change the entire page and will create a whole new one.
The left side, don't need to have a Navigator, only handle it using Widgets states(StatefullWidget or something like that).
SplitView(
menu: MenuSideBar(
onChangeSection: (Widget section) {
setState((){
pages.last=section;
});
}),
content: Navigator(
pages: pages,
),
),

Flutter Modal Bottom Sheet with Navigator does not pop as expected

I a working with ModalBottomSheet and I love the package. However I am encountering an issue when trying to navigate with in a ModalBottomSheet.
Here is a ScreenVideo for a better understanding.
As you can see the View is presented as a ModalBottomSheet. But when popping it, it is not simply dismissing to the bottom but instead it is popping to some empty screen with a MaterialPage Pop animation.
I changed my code so I can push with a MaterialPageRoute-Animation inside that ModalBottomSheet. Before that everything was working as expected.
Here is my Page:
class _AboutScreenState extends State<AboutScreen> {
#override
Widget build(BuildContext context) {
return Material(
color: AppColors.primary,
child: Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context2) => Builder(
builder: (context) => CupertinoPageScaffold(
backgroundColor: AppColors.secondary,
resizeToAvoidBottomInset: false,
child: SafeArea(
child: Padding(
padding: EdgeInsets.all(sidePadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButtonWithExpandedTouchTarget(
onTapped: () {
Navigator.pop(context);
},
svgPath: 'assets/icons/down.svg',
),
],
),
Text(
'About',
style: AppTextStyles.montserratH2SemiBold,
),
...
RoundedCornersTextButton(
title: 'Impressum',
onTap: () {
Navigator.pop(context);
},
textColor: AppColors.primary,
backgroundColor: AppColors.secondary,
borderColor: AppColors.primary,
),
],
),
),
)),
),
),
),
);
}
}
I was simply following this example. What am I missing here? With the code above I can push inside the ModalSheet as expected with a MaterialPageRoute Animation but popping the first screen brings up the issue...
Let me know if you need any more info! Any help is appreciated :)
I think you are popping with the wrong context. The example is popping with rootContext, which is from the top-most widget in the hierarchy. You are popping from with context, defined at your lowest builder in the hierarchy.
I believe you are using the incorrect context. rootContext, which is from the top-most widget in the hierarchy, is what makes the sample pop. You're popping from your lowest builder in the hierarchy, which is dictated by context.

OverlayEntry that captures PointerEvents, but also forwards them to the main app

Here, I am trying to create an OverlayEntry on top of the app that should:
Capture, but also forward pointer events
Render some information
The essence is:
Overlay.of(context).insert(
OverlayEntry(
builder: (context) => Listener(
behavior: HitTestBehavior.translucent,
onPointerMove: (event) => print(event),
child: Text('Some more debug information'),
),
),
);
However, the pointer events DON'T get forwarded to the app, but are "swallowed" by the Overlay.
It turned out easier than expect. I just had to wrap the child of Listener into IgnorePointer:
#override
void initState() {
super.initState();
scheduleMicrotask(
() => Overlay.of(context).insert(
OverlayEntry(
builder: (context) => Listener(
behavior: HitTestBehavior.translucent,
onPointerMove: (event) => print(event),
child: IgnorePointer(
child: Text('Some more debug information'),
),
),
),
),
);
}
Still, the line with behavior: HitTestBehavior.translucent is required. If I remove that line, I can't observe the events.

Navigator and Provider conflict in flutter

sending data between screen using Provider with Navigator concept make a conflict
error after the run
The following ProviderNotFoundError was thrown building SecondRoute(dirty):
Error: Could not find the correct Provider above this SecondRoute Widget
To fix, please:
Ensure the Provider is an ancestor to this SecondRoute Widget
Provide types to Provider
Provide types to Consumer
Provide types to Provider.of()
Always use package imports. Ex: `import 'package:my_app/my_code.dart';
Ensure the correct context is being used.
https://www.ideone.com/xHXK5m
you can pass data like this put data in class
RaisedButton(
child: Text(‘Send data to the second page’),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(
data: data,
)),
);
},
),
and recieve data like this
class SecondPage extends StatelessWidget {
final Data data;
SecondPage({this.data});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Constructor — second page’),
),
body: Container(
padding: EdgeInsets.all(12.0),
alignment: Alignment.center,
child: Column(
children: <Widget>[
Container(
height: 54.0,
padding: EdgeInsets.all(12.0),
child: Text(‘Data passed to this page:’,
style: TextStyle(fontWeight: FontWeight.w700))),
Text(‘Text: ${data.text}’),
Text(‘Counter: ${data.counter}’),
Text(‘Date: ${data.dateTime}’),
],
),
),
);
}
You can try placing whatever Provider value you have above the MaterialApp and thus the navigation or, when you push to a second page, provide the value again. Providers are scoped to widget trees, so this is expected behavior.
As an example, to pass a value to another route, you could do something like
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
final foo = 'foo';
Provider<String>.value(
value: foo,
child: NewPage(),
);
}));
},
And then just consume it like regular in the NewPage route.
Try this code:
MaterialApp(
title: 'Navigation Basics',
home: ChangeNotifierProvider(
builder: (context) => Data(),
child: FirstRoute(),
))
Delete ChangeNotifierProvider in FirstRoute