No MediaQuery ancestor could be found? - flutter

I'm trying to build a Container that is a third of size of the page but i'm getting an error No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of(). and I'm not sure why at all. Its in MaterialApp.
My Code:
import 'package:flutter/material.dart';
void main() => runApp(LoginPage());
class LoginPage extends StatelessWidget{
#override
Widget build(BuildContext context){
return MaterialApp(
home: Scaffold(
body: Container(
constraints: BoxConstraints.expand(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.red,
width: double.infinity,
height: MediaQuery.of(context).size.height/3,
)
],
)
)
)
);
}
}

You just have to give the MaterialApp as the ancestor..as the error says..
Do it like this..
void main() => runApp(MaterialApp(home:LoginPage()));

Your must have a MaterialApp widget because you are using the Material class from your import statement
Check the code below, it works fine:
import 'package:flutter/material.dart';
// wrap your LoginPage widget with a MaterialApp widget
void main() => runApp(MaterialApp(home:LoginPage()));
class LoginPage extends StatelessWidget{
#override
Widget build(BuildContext context){
return MaterialApp(
home: Scaffold(
body: Container(
constraints: BoxConstraints.expand(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.red,
width: double.infinity,
height: MediaQuery.of(context).size.height/3,
)
],
)
)
)
);
}
}

None of the solutions worked for me. It was because the showModalBottomSheet tries to access the ancestor of type MaterialApp from the given context.
Use Builder widget to get new context with MaterialApp ancestor
OR
Separate your MaterialAapp and Scaffold widgets into separate widgets.
My solution using Builder :
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
child: Icon(Icons.add),
onPressed: () { showModalBottomSheet(
context: context,
builder: (context) {
return Text('Modal bottom sheet', style: TextStyle(fontSize: 30));
});
}
),
),

to add to this, there are cases where the generated test case is not updated to match your current main class

Related

How to use Custom Widgets in flutter

I am new to Flutter and I am following Flutter's official tutorial to learn basics of flutter.
So, I want to create a reusable component named "CustomCard" and I did this:
class CustomCard extends StatelessWidget {
CustomCard({#required this.index, #required this.onPress});
final index;
final Function onPress;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
FlatButton(
child: const Text('Press'),
onPressed: this.onPress,
)
],
),
);
}
}
Now, to use it in MyApp, I did this:
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: 'Welcome to flutter',
home: Scaffold(
appBar: AppBar(
title: Text('hello'),
),
body: Center(
child: Text('centre'),
CustomCard(
index:'card1',
onPress: print(' this is $index')
),
),
),
);
}
}
Now my IDE says that:
The method 'CustonCard' isn't defined for the class 'MyApp'.
How to solve this?
Error in terminal:
Compiler message:
lib/main.dart:17:6: Error: Place positional arguments before named arguments.
Try moving the positional argument before the named arguments, or add a name to the argument.
CustomCard(
^^^^^^^^^^
lib/main.dart:17:6: Error: Expected named argument.
CustomCard(
^
lib/main.dart:15:21: Error: No named parameter with the name '#1'.
body: Center(
^^...
/C:/src/flutter/packages/flutter/lib/src/widgets/basic.dart:1863:9: Context: Found this candidate, but the arguments don't match.
const Center({ Key key, double widthFactor, double heightFactor, Widget child })
Edit: corrected a spelling mistake. Also adding console log.
You have got a few mistakes going on, but don't worry it happens at the very beginning. So let's step into the problems:
First, inside the body you've defined a Center Widget which only allows a single child within it but you have tried to put two Widgets (Text and CustomCard). So to put both widgets you could change it to something like this:
Center(
child: Column(
children: <Widget>[
Text('centre'),
CustomCard(...)
],
),
),
Moreover, pay attention that the onPress function takes a Function as argument but you are passing the result of print(...) which is void. Simply change it to:
CustomCard(index: index, onPress: () => print(' this is $index'))
Finally, I think you missed to define the index variable. Simply add:
String index = "card1";
Hey Please check the updated code here. There are couple of compile errors as you are wrapping up both Text and you CustomWidget in Center where it accepts only one child widget and also at the onPress method some code change required.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to flutter',
home: Scaffold(
appBar: AppBar(
title: Text('hello'),
),
body: Center(
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('centre'),
CustomCard(
index:'card1',
onPress: onPress
)
],
)
)
),
);
}
onPress(index){
print("this is $index");
}
}
class CustomCard extends StatelessWidget {
CustomCard({#required this.index, #required this.onPress});
final index;
final Function onPress;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
FlatButton(
child: const Text('Press'),
onPressed: (){
this.onPress(index);
},
)
],
),
);
}
}
child: Center(
child: Row(
children: <Widget>[
_box(), //Custom pin view
_box(), //Custom pin view
_box(), //Custom pin view
_box(),//Custom pin view
],
),
)
and Custom Widgets (_box) Code
Widget _box() {
return Container(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
alignment: Alignment.center,
height: 50,
width: 50,
child: TextField(
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(border: InputBorder.none, counterText: ''),
),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}

Which layout structure should I code to match my app design

For my new Flutter project I've done some research on layouts, however it still is a bit vague.
Here is the layout I'd like to create:
After some research and watching some videos, it seems this is the MaterialApp code:
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Color(0xFF0E2E40),
title: Text('City Discovery'),
actions: [
Image.asset(
'assets/images/logoicon.png',
width: 35,
)
],
),
body: ListView(
children: [
introSection,
Image.asset(
'assets/images/barman.png',
width: 600,
height: 240,
fit: BoxFit.cover,
),
crimeSection,
mysterySection,
bottomSection,
],
),
),
);
}
But now defining the layout of the different Sections is quite hard.
From what I can tell, there should be:
ListView to make my page scrollable, which it should be
introSection: Container for XYZ with 2 rows for 2 text sections.
Image
crimeSection: Container for XYZ with 3 rows for 2 text sections and button. First text section divided into 2 colums for icon and text
mysterySection: Container for XYZ with 2 rows for 2 text sections. First text section divided into 2 columns for icon and text
bottomSection: Tabs? Although tabs only seem to be part of the appbar, so not sure what the alternative is
The closest example I could find, however changing the code doesn't get me anywhere:
https://github.com/flutter/website/blob/master/examples/layout/lakes/step6/lib/main.dart
For some reason containers can only have 1 child, so that probably means I need a row in my container which then has children which then has 2 childs for both text sections? Incredibly hard to code some simple UI.
If someone can help me with the code for the introSection and the bottomSection, I can probably work out the rest myself.
I recommend to create components folder for reusable widgets, as buttons, logos, text, etc.
If you have a widget that does not used in other components, you can separate it in the same file but use underscore, to make this widget private (It will be visible only inside this file). All that will make your code more readable.
About bottom tabs.
If you want to use it as bottom tabs, you can put it in bottomNavigationBar parameter of Scaffold widget. If you need to use in inside the ListView, it possible but you need to pass the exact height of the tab, because TabView trying to expand as much as possible, same as ListView.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LayoutExample(),
);
}
}
class LayoutExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _BottomNavigationBar(),
body: ListView(
children: <Widget>[
_IntroSection(),
Placeholder(fallbackHeight: 200),
_CrimeSection(),
_MysterySection()
],
),
);
}
}
class _IntroSection extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 150,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('Intro text1'),
Text('Intro text2')
],
),
);
}
}
class _CrimeSection extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
),
),
height: 200,
child: Text('CrimeSection'),
);
}
}
class _MysterySection extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blueAccent,
),
),
height: 200,
child: Text('MysterySection'),
);
}
}
class _BottomNavigationBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BottomAppBar(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
)
],
),
);
}
}

How to arrange boxes in row in Flutter?

This is my code to arrange boxes vertically.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
Widget box({width: 100.0, height: 100.0}) => Container(
width: width,
height: height,
color: Colors.grey,
);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
}
}
Since I have a little experience for CSS flexbox, replacing the Column widget with the Row to arrange boxes in row But it can't work. Only black screen appeared. I've already make sure that the code works under the MaterialApp widget.
For learning purpose, I want to keep the code as simple as possible.
What do I need to make the above code work? What is a minimum requirement? Or is there an other widget appropriates for root in such example case?
Thank you for sparing time to read my question!
You need a material app render the colors and stuff
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
);
}
}
MaterialApp widget must be a root widget
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp())); // <- magic
class MyApp extends StatelessWidget {
Widget box({width: 100.0, height: 100.0}) {
return Container(
width: width,
height: height,
color: Colors.grey,
);
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
box(),
box(),
box(),
],
),
);
}
}

Use nested Navigator with WillPopScope in Flutter

In my application, I want to have a custom Navigation (only change part of the screen and keep an history of what I'm doing inside it).
For that purpose, I am using a Navigator and it's working fine for simple navigation.
However, I want to handle the back button of Android.
There is a problem with it in Flutter apparently which forces me to handle the backbutton in the parent widget of the Navigator :
https://github.com/flutter/flutter/issues/14083
Due to that, I need to retrieve the instance of my Navigator in the children and call pop() on it. I am trying to use a GlobalKey for this.
I am trying to make it work for a while now and made a sample project just to test this.
Here is my code :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(title: 'Navigation Basics', home: MainWidget()));
}
class MainWidget extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
#override
Widget build(BuildContext context) {
return SafeArea(
child: WillPopScope(
onWillPop: () => navigatorKey.currentState.maybePop(),
child: Scaffold(
body: Padding(
child: Column(
children: <Widget>[
Text("Toto"),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('First'),
onPressed: () {
navigatorKey.currentState.pushNamed('/first');
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => SecondRoute()),
// );
},
)),
Expanded(
child: RaisedButton(
child: Text('Second'),
onPressed: () {
navigatorKey.currentState.pushNamed('/second');
},
))
],
),
Expanded(
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(color: Colors.red),
),
ConstrainedBox(
constraints: BoxConstraints.expand(),
child: _getNavigator()),
],
)),
],
),
padding: EdgeInsets.only(bottom: 50),
))));
}
Navigator _getNavigator() {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case '/':
builder = (BuildContext _) => FirstRoute();
break;
case '/second':
builder = (BuildContext _) => SecondRoute();
break;
default:
throw new Exception('Invalid route: ${settings.name}');
}
return new MaterialPageRoute(builder: builder, settings: settings);
});
}
}
class FirstRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("GO TO FRAGMENT TWO"),
onPressed: () => Navigator.of(context).pushNamed("/second"),
)
],
),
decoration: BoxDecoration(color: Colors.green),
);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("GO TO FRAGMENT ONE"),
onPressed: () => Navigator.of(context).pop(),
)
],
),
decoration: BoxDecoration(color: Colors.blue),
);
}
}
This is however not working as I would like. The default Navigator seem to still be used : after opening the SecondRoute and pressing the Android back button, it just leaves the app instead of just going back to the first route.
How can I achieve what I want?
Following the documentation of onWillPop:
/// Called to veto attempts by the user to dismiss the enclosing [ModalRoute].
///
/// If the callback returns a Future that resolves to false, the enclosing
/// route will not be popped.
final WillPopCallback onWillPop;
your handler should indicate that the enclosing route should not be closed, hence returning false will resolve your issue.
changing your handler to this works:
onWillPop: () async {
navigatorKey.currentState.maybePop();
return false;
},

Child widget of CupertinoPageScaffold is behind CupertinoNavigationBar

I'm trying to put contents below a CupertinoNavigationBar.
But the content Widgets are partially covered by the CupertinoNavigationBar.
I don't understand why the child Column is not vertically offset so that the top isn't covered by the CupertinoNavigationBar.
Below is a screenshot, and my code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PersonalInfoEditor extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: buildNavigationBar(context),
child: Column(
children: <Widget>[
Text('Personal Info'),
Text('t1'),
],
),
);
}
CupertinoNavigationBar buildNavigationBar(BuildContext context) {
return CupertinoNavigationBar(
trailing: CupertinoButton(
child: Text('Save', style: TextStyle(color: CupertinoColors.activeBlue)),
onPressed: () => Navigator.pop(context),
));
}
}
Wrap your child inside SafeArea :
return CupertinoPageScaffold(
navigationBar: buildNavigationBar(context),
child: SafeArea(
child: Column(
children: <Widget>[
Text('Personal Info'),
Text('t1'),
],
),
),
);
And don't forget to use CupertinoApp instead of MaterialApp
More info: https://api.flutter.dev/flutter/cupertino/CupertinoPageScaffold-class.html