No MediaQuery widget ancestor found while using MediaQuery.of(context) - flutter

I am getting an error saying No MediaQuery widget ancestor found while using MediaQuery.of(context). Here is my code and I am using MediaQuery.of(context) inside a MaterialApp but it is still giving me errors. Can someone help?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Responsive'),
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
color: Colors.red,
height: mediaQuery.size.height,
),
Container(
color: Colors.amber,
height: 200,
),
],
),
),
),
);
}
}
Error

You get this error because your MediaQuery is out of MaterialApp. Use material home as seperate Stateless of Statefull widget.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
return MaterialApp(
home: Home();
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
return Scaffold(
appBar: AppBar(
title: Text('Responsive'),
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
color: Colors.red,
height: mediaQuery.size.height,
),
Container(
color: Colors.amber,
height: 200,
),
],
),
),
);
}
}

you need to use media Query like that
Size size = MediaQuery.of(context).size;
and where you want to use so like this according width or height
height: size.height*0.1,

Related

I am trying to build the main.dart file but getting this error

import 'package:expense_management/widgets/user_transaction.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter App'),
),
body: Column(
children: <Widget>[
Container(
width: double.infinity,
child: const Card(
color: Colors.orange,
elevation: 5,
child: Text('chart'),
),
),
userTransaction()
],
));
}
}
error--------------------
The following Unimplemented Error was thrown building Keyed
Subtree-[GlobalKey#87727]: UnimplementedError
The relevant error-causing widget was: Scaffold
Scaffold:file:///C:/Users/abhargaw/Downloads/development/projects/expense_management/lib/main.dart:21:1
2

Align widget center with AppBar bottom edge

I need to align a widget's center with the appbar bottom edge.
So it will be located vertically half on the appbar and half on the page body.
Right now I've added the widget into the AppBar bottom: but it wont align with it's horizontal center line.
Currently It looks like this:
While i want that the center of the SelectEnvironment button along with the horizontal white line will 'sit' exactly on the bottom edge of the appBar
The code for the appBar is like this:
class CustomAppBar extends AppBar {
final Widget appBarActionButton;
CustomAppBar({Widget title = AppUtils.EMPTY_TEXT_VIEW, this.appBarActionButton}): super(
title: title,
backgroundColor: Colors.blueGrey,
elevation: 0,
bottom: PreferredSize(
child: Stack( //The stack holds the horizontal line and the button aligned cente
alignment: Alignment.center,
children: <Widget>[
Container( //This is the horizontal line
color: Colors.GeneralDividerGray,
height: 1.0,
),
Align(
child: Container(
child: appBarActionButton, //This is the button widget
),
)
],
),
preferredSize: Size.fromHeight(4.0)),
);
}
If there a better way to achieve this by taking it outside of the appbar it's ok with me as long it will give the same effect.
I think you should use Stack and Column like this
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
typedef void OnWidgetSizeChange(Size size);
class MeasureSize extends StatefulWidget {
final Widget child;
final OnWidgetSizeChange onChange;
const MeasureSize({
Key key,
#required this.onChange,
#required this.child,
}) : super(key: key);
#override
_MeasureSizeState createState() => _MeasureSizeState();
}
class _MeasureSizeState extends State<MeasureSize> {
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
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> {
Size s;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
MeasureSize(
onChange: (size) {
setState(() {
s = size;
});
},
child: AppBar(
title: Text('title'),
),
),
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - (s?.height ?? 0.0),
child: Center(child: Text('body')))
],
),
Positioned(
top: (s?.height ?? 0.0) - 16.0,
child: Container(
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 32,
color: Colors.red[400],
padding: EdgeInsets.all(6),
child: Center(child: Text('Select Environment'))),
],
),
),
)
],
));
}
}
The best way is to use Slivers via a widget like below:
ScrollController scrollController = new ScrollController();
return Stack(
children: [
NestedScrollView(
controller: scrollController,
headerSliverBuilder: (context, value){
return [
// list of widgets in here
];
},
body: Container(
// here, your normal body goes
),
),
Positioned(
top: 50.0,
left: 100.0,
child: Container(
// your centered widget here
),
)
]
);
}
Instead of using a normal appBar, you have to use a SliverAppBar

MaterialApp and "MediaQuery.of() called with a context that does not contain a MediaQuery"

I am recieving the following error When i attempt to use MediaQuery in my App:
"MediaQuery.of() called with a context that does not contain a MediaQuery"
I have done some research and everything i read says that if i use either MaterialApp or WidgetsApp i should have access to MediaQuery.
However I am using MaterialApp as a parent widget from the location i am using MediaQuery... so i dont understand why i dont have access to the MediaQuery in the context that i am using...??
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
List<Map<String, String>> elementList;
String elementName;
MyApp() {
this.elementName = "TestName";
this.elementList = List<Map<String, String>>();
this.elementList.add({"element1": "Value1"});
this.elementList.add({"element2": "Value2"});
this.elementList.add({"element3": "Value3"});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.15,
child: UNavbar(),
),
Container(
height: MediaQuery.of(context).size.height * 0.7,
child: ListView.builder(
itemBuilder: (ctx, index) {
return UNode(elementName, elementList);
}
)
),
Container(
height: MediaQuery.of(context).size.height * 0.15,
alignment: Alignment.bottomCenter,
child: UTextbox(),
),
],
),
);
}
}```
I think MediaQuery require any parent widget which have Mediadata as Materialapp have, so if you create a separate widget which contain MaterialApp widget and then put all other code in separate widget. it will work.
Note: I replaced all your widget with text widget and i wrap column with scaffold other wise you will get black screen.
Following code help you more about it.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
List<Map<String, String>> elementList;
String elementName;
MyApp() {
this.elementName = "TestName";
this.elementList = List<Map<String, String>>();
this.elementList.add({"element1": "Value1"});
this.elementList.add({"element2": "Value2"});
this.elementList.add({"element3": "Value3"});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home());
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.15,
child: Text(""),
),
Container(
height: MediaQuery.of(context).size.height * 0.7,
child: ListView.builder(itemBuilder: (ctx, index) {
return Text("");
})),
Container(
height: MediaQuery.of(context).size.height * 0.15,
alignment: Alignment.bottomCenter,
child: Text("data"),
),
],
));
}
}
The issue is happening because the context you are using in the MediaQuery won't have a MaterialApp associated with that. It's passed as an argument of the build method and you are creating the material app inside the same build method. To fix this you can use the Builder widget.
home: Builder(builder: (context) {
return Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.15,
child: UNavbar(),
),
// Remaining Children
],
);
),

Flutter Web persistent header

With the introduction of flutter for web it has me trying to achieve a website style header that is persistent when using routes and across the entire app. Appbar doesn't appear to be the solution since each scaffold has its own appBar. I've created the header widget that's in a Column with the MaterialApp. However, this implementation feels wrong as everything should be a child of MaterialApp or CupertinoApp.
If the searchBar header can be placed within the MaterialApp and I'm able to use Navigator that's would be preferred. I'm really here for guidance and the "right" way to do this.
void main() {
initKiwi();
// BlocSupervisor().delegate = AppBlocDelegate();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Material(
elevation: 2.0,
color: Colors.white,
child: MediaQuery(
data: MediaQueryData.fromWindow(ui.window),
child: Directionality(
textDirection: TextDirection.ltr,
child: Container(
height: 50,
child: SearchBar(),
),
),
),
),
Expanded(
child: MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl',
),
home: Text("Pages & Routes Here"),
),
),
]);
}
}
Though it's not using routes I was able to solve this using IndexedStack. This also preserves any scrolling I've done in the ProductsPage() when closing the search page. The AppBar is persistent and was able to keep the code to a minimum.
main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Discover Brindle',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Brdl'
),
home: MainPage(),
);
}
}
main_page.dart
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final _searchBloc = kiwi.Container().resolve<SearchBloc>();
final _productsBloc = kiwi.Container().resolve<ProductsBloc>();
PageController pageController;
int currentPage = 0;
void _onSearchActive({bool isActive}) {
setState(() {
this.currentPage = isActive ? 1 : 0;
});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: _buildScaffold(),
);
}
Widget _buildScaffold() {
return BlocProviderTree(
blocProviders: [
BlocProvider<SearchBloc>(bloc: _searchBloc),
BlocProvider<ProductsBloc>(bloc: _productsBloc),
],
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: SearchBar(onIsActive: _onSearchActive),
),
body: IndexedStack(
children: [
ProductsPage(),
SearchPage(),
],
index: currentPage,
),
),
);
}
}

flutter's AutomaticKeepAliveClientMixin doesn't keep the page state after navigator.push

was testing AutomaticKeepAliveClientMixin and run into an issue,
page loses state after navigator.push
anyone knows this issue? any workarounds? be glad for any info, cheers
my goal is to keep the page state
steps to reproduce: open app click PageOne's push-button then go back swipe right and left and the page loses state
image
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
initialIndex: 0,
length: 2,
child: Scaffold(
body: TabBarView(
children: <Widget>[Page1(), Page2()],
),
bottomNavigationBar: Material(
child: TabBar(
labelColor: Colors.black,
tabs: <Widget>[
Tab(
icon: Icon(Icons.check),
),
Tab(
icon: Icon(Icons.check),
),
],
),
),
),
),
);
}
}
class Page1 extends StatefulWidget {
#override
Page1State createState() {
return new Page1State();
}
}
class Page1State extends State<Page1> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
height: 300,
color: Colors.orange,
),
Container(
height: 300,
color: Colors.pink,
),
Container(
height: 300,
color: Colors.yellow,
child: Center(
child: Container(height: 26,
child: MaterialButton(
color: Colors.blue,
child:
Text('clicking this and back then swipe => page loses state'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PushedPage()),
);
}),
),
),
),
],
);
}
#override
bool get wantKeepAlive => true;
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(height: 300, color: Colors.orange);
}
}
class PushedPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.blue,
),
);
}
}
From the documentation on AutomaticKeepAliveClientMixin:
A mixin with convenience methods for clients of
[AutomaticKeepAlive]. Used with [State] subclasses.
Subclasses must implement [wantKeepAlive], and their [build]
methods must call super.build (the return value will always return
null, and should be ignored).
So in your code, before you return the ListView just call super.build:
Widget build(BuildContext context) {
super.build(context);
return ListView(...
}