Flutter multiple `MaterialApp` lead to conflict - flutter

two MaterialApp lead to navigator occur error, because I can only modify outermost layer MaterialApp , I remove outermost layer MaterialApp, it tell me 'MediaQuery.of() called with a context that does not contain a MediaQuery.' What can I do ?
return MaterialApp(
navigatorKey: navigatorKey,
home: Scaffold(
body: Stack(
alignment: Alignment.center,
children: [
RepaintBoundary(
child: child,
key: rootKey,
),
_ContentPage()
]
)
)
);

Related

In Flutter, how to scroll both AppBar and nested Beamer

I'm using Beamer for navigation in Flutter, and I want the top AppBar to not change (or do transition animations) when you navigate between screens. To do this, I'm using a nested Beamer. The build method for the top level screen looks like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('some title')),
body: Beamer(
key: _beamerKey,
routerDelegate: routerDelegate,
),
);
}
The result with this is that the AppBar is fixed at the top of the view port. If child screens have ScrollViews then just their content scrolls. I want the AppBar to scroll too to maximize screen real estate on small screens. I have tried the following:
return Scaffold(
body: ListView(
children: [
AppBar(title: const Text('some title')),
Beamer(
key: _beamerKey,
routerDelegate: routerDelegate,
),
],
));
and
return Scaffold(
body: CustomScrollView(slivers: <Widget>[
const SliverAppBar(
title: Text('data'),
),
SliverList(
delegate: SliverChildBuilderDelegate(
((context, index) => Beamer(
key: _beamerKey,
routerDelegate: routerDelegate,
)),
childCount: 1)),
]),
);
In both cases I get an error _AssertionError ('package:flutter/src/widgets/overlay.dart': Failed assertion: line 728 pos 12: 'constraints.biggest.isFinite': is not true.)
What am I doing wrong?
Here's an example using a list of text fields in case it's not clear what it should look like (the app bar just scrolls along with everything else under it):

Make nested navigator widget wrap child in Column

How can I achieve this where a nested Navigator widget in a Column wraps the child content.
This crashes the app
Column(
children: [
Text("First Text"),
Navigator(
key: _navigatorKey,
initialRoute: widget.setupPageRoute,
onGenerateRoute: _onGenerateRoute,
),
Text("Second Text"),
],
),
with error
'package:flutter/src/widgets/overlay.dart': Failed assertion: line 720 pos 12: 'constraints.biggest.isFinite': is not true.
but adding a Flexible around the Navigator doesnt respect the flex and it just expands.
Column(
children: [
Text("First Text"),
Flexible(
child: Navigator(
key: _navigatorKey,
initialRoute:widget.setupPageRoute,
onGenerateRoute: _onGenerateRoute,
),
),
Text("Second Text"),
],
),
Any ideas on how this can be solved? Thanks in advance
Before the issue got fixed, you can wrap IntrinsicHeight outside of the Navigator.
You only need to be careful with the notice in the document:
This class is relatively expensive, because it adds a speculative
layout pass before the final layout phase. Avoid using it where
possible. In the worst case, this widget can result in a layout that
is O(N²) in the depth of the tree.
Column(
children: [
Text("First Text"),
IntrinsicHeight (
child: Navigator(
key: _navigatorKey,
initialRoute:widget.setupPageRoute,
onGenerateRoute: _onGenerateRoute,
),
),
Text("Second Text"),
],
)

How to push a widget outside of context?

My current screenstructure looks like this:
Scaffold(
body: Stack(
children: [
_buildOffstageNavigator(FeedScreen()),
_buildOffstageNavigator(Screen2()),
_buildOffstageNavigator(Screen3()),
],
),
bottomNavigationBar: buildBottomNavigationBar(),
)
the BottomNavigationBar is just a regular bar.
Each of those screens are wrapped with a OffStage() widget like this:
Widget _buildOffstageNavigator(Widget screenToBuild) {
return Offstage(
offstage: currentPage != screenToBuild,
child: TabNavigator(
navigatorKey: _navigatorKeys[screenToBuild],
screenToBuild: screenToBuild,
),
);
}
My intention with this was to ensure a BottomNavigationBar at all times.
However, there is one widget, that I want to push from FeedScreen() that I don't want the NavigationBar on, but the context makes it hard to hide this NavigationBar
This is the structure of the FeedScreen()
Scaffold(
appBar: buildAppBar(),
body: SingleChildScrollView(
key: PageStorageKey<String>("Feed"),
controller: _feedSC,
child: Column(
children: [
buildStoriesListView(),
buildPostsListView(posts)
],
),
),
)
So my question is: Is there a way to push this Widget "on top" of the stack and kind of ignore the context or do I just have to hide the bar in this case? How would you do that?
Thanks
You can wrap you Scaffold with a Navigator and use Navigator.of(context, rootNavigator: true).push(PageRouteBuilder(pageBuilder: (context, _, __) => Foo())); to push the Foo widget on top of everything.

Calculating 100% height in flutter

I found some topics about calculating height in flutter, but noone of them answered my question. I am trying to calculate 100% of device height in flutter. I assume, I have to subtract from MediaQuery.of().size.height two things. First is AppBar height, so I calculated it by creating variable appBar and get property preferredSize.height. Second is the bar above appBar (which contains things like battery status, notifications etc.).
This is the simplest example:
#override
Widget build(BuildContext context) {
var appBar = AppBar(
title: Text(widget.title),
);
return Scaffold(
appBar: appBar,
body: Center(
child: Builder(
builder: (ctx) => Column(
children: <Widget>[
Container(
height: MediaQuery.of(ctx).size.height -
MediaQuery.of(ctx).padding.top -
appBar.preferredSize.height,
color: Colors.red,
)
],
),
),
),
);
}
This is my main Widget so I had to create context with Builder. However, it is not still equal to 100% of device height. It is 24px too much. I don't want to subtract 24px because It might be more/less on another device. What is wrong with this example?
You don't need to do anything special to the child of the Scaffold. Unless the child widget manages its own sizing, it will fill the entire remainder of the area inside of the Scaffold by default. In the case of Container, its behavior is to shrink wrap to the size of its child, but if it doesn't have a child, it will fill all available space. So you can just do this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Container(
color: Colors.red,
),
);
}
Depending on the child widget, you may need to manually configure a size, but most layout widgets either you won't need to worry about it or it just takes a bit of configuration. With Column, for example, if you set mainAxisSize: MainAxisSize.max and crossAxisAlignment: CrossAxisAlignment.stretch, then it will also automatically stretch to fill all available space. You can then make use of Expanded to size its children based on available space as well.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(color: Colors.red),
),
Expanded(
child: Container(color: Colors.green),
),
Expanded(
flex: 2,
child: Container(color: Colors.yellow),
),
],
),
);
}
However, on the seldom occasion you need the exact numbers of the space available, you don't usually want to use MediaQuery as that will get you the size of the entire screen, not just the available space, and you usually will have to perform additional calculations to boil it down to the size you are actually looking for (which usually leads to incorrect results, as you have discovered).
Instead, you can use a LayoutBuilder. A word of caution here, you don't want to abuse LayoutBuilder as it adds an additional layout step to its children, so having a lot of them in your code can easily slow down your app.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: LayoutBuilder(
builder: (context, constraints) {
final width = constraints.biggest.width;
final height = constraints.biggest.height;
return Center(
child: Text('Width: $width, Height: $height'),
);
},
),
);
}

Responsive element alignment with MediaQuery - called with a context that does not contain it

I'm currently designing an app with flutter in dart, and I've been using the Flutter Speed Dial package trying to align it to the bottom-left of the screen using the marginRight and marginBottom values. marginBottom is easy enough, but the problem with marginRight is that depending on the screen size, the same static value might end up putting the widget at the center, all the way left, or even off the screen depending on the size of the screen.
So I called a mediaQuery to get the screen size and base the widget off that, margin just slightly less than the width of whichever screen it is on, to make the value responsive.
However, it says that MediaQuery is called out of context, even though I'm calling it in SpeedDial, which is under FloatingActionButton of Scaffold, set as home of a MaterialApp like so:
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
body: Stack(
children: [...]
),
floatingActionButton:
SpeedDial(
marginRight: MediaQuery.of(context).size.width - 20,
marginBottom: 65,
[...]
children: [
SpeedDialChild(
[...]
),
SpeedDialChild(
[...]
),
],
),
)
);
}
The full error detailed below:
MediaQuery.of() called with a context that does not contain a MediaQuery. No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of(). This can happed because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
Thanks in advance, this has been rather frustrating :) any help is much appreciated.
To fix your issue just wrap your main Widget as a home of your MaterialApp widget, like this:
main.dart
void main() {
runApp(
MaterialApp(home: YourWidget()),
);
}
And remove the MaterialApp widget here
yourwidget.dart
class YourWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [...]
),
floatingActionButton:
SpeedDial(
marginRight: MediaQuery.of(context).size.width - 20,
marginBottom: 65,
[...]
children: [
SpeedDialChild(
[...]
),
SpeedDialChild(
[...]
),
],
),
);
}
}
You are losing context as Speeddial is using its own context. to get context back you can use a builder.
Or modify SpeedDial to accept a BuildContext.
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
body: Stack(
children: [...]
),
floatingActionButton: Builder(context) {
return SpeedDial(
marginRight: MediaQuery.of(context).size.width - 20,
marginBottom: 65,
[...]
children: [
SpeedDialChild(
[...]
),
SpeedDialChild(
[...]
),
],
),
}
)
);
}