Flutter's MediaQuery rectangle seems incorrect - flutter

A Rect with a top-left at (0, 0) and sized MediaQuery.of(context).size should match exactly the rectangle left after the AppBar rectangle is present.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(scaffoldBackgroundColor: const Color(0xFF80EFEF)),
home: Scaffold(
appBar: AppBar(
title: Text('MyApp'),
),
body: MyWidget()));
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
var deviceData = MediaQuery.of(context);
return CustomPaint(painter: MyPainter(appSize: deviceData.size));
}
}
class MyPainter extends CustomPainter {
Size appSize;
MyPainter({this.appSize});
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = Colors.indigo;
const double margin1 = 70;
canvas.drawRect(
Rect.fromLTWH(margin1, margin1, appSize.width - 2 * margin1,
appSize.height - 2 * margin1),
paint);
}
#override
bool shouldRepaint(MyPainter oldDelegate) => false;
}
As you see here:
it does not. It protrudes (when targeting chrome) from the bottom. We're only able to see that when we use a margin to reduce the rectangle's size.
Why does the rectangle not match the expected area? Is this a bug?

You can use layoutbuilder to know the exact remaining space left.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('LayoutBuilder Example')),
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Text('Width: ${constraints.maxWidth} Height: ${constraints.maxHeight}');
},
),
);
}

When we write a Widget class such as
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
var deviceData = MediaQuery.of(context);
return CustomPaint(painter: MyPainter(appSize: deviceData.size));
}
}
and make a MediaQuery.of(), the context that we are querying is not that of MyWidget. It is the context of the parent of MyWidget in the widget objects tree.
In this case the parent is the Scaffold. Hence the context we get does include the AppBar!
(To make it clearer, it would perhaps be more apt to write parentContext, rather than context.)
The solution is to add an intermediate "dummy" parent widget that has the correct dimensions.
This does not need to be a full-fledged new class. Using either Builder or LayoutBuilder is enough.
Using Builder doesn't cut it (why?)
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: new ThemeData(scaffoldBackgroundColor: const Color(0xFF80EFEF)),
home: Scaffold(
appBar: AppBar(
title: Text('MyApp'),
),
body: Builder(
builder:
(BuildContext context) {
return MyWidget();
},
)));
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
var deviceData = MediaQuery.of(context);
return CustomPaint(painter: MyPainter(appSize: deviceData.size));
}
}
Hence we need to use, as in Ayad's answer, LayoutBuilder.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: new ThemeData(scaffoldBackgroundColor: const Color(0xFF80EFEF)),
home: Scaffold(
appBar: AppBar(
title: Text('MyApp'),
),
body: LayoutBuilder(
builder:
(BuildContext context, constraints) {
return MyWidget(size: Size(constraints.maxWidth, constraints.maxHeight));
},
)));
}
}
class MyWidget extends StatelessWidget {
Size size;
MyWidget({this.size}) {}
#override
Widget build(BuildContext context) {
return CustomPaint(painter: MyPainter(appSize: size));
}
}
We then see that we have the correct Size.

Related

Flutter: Why the hashCodes of the following container widgets are changing eveytime I hot-reload the app (save the file)?

If I run the following application and observe the hashCodes for BuildSizedBoxWidget which I create two instances of, I notice that they are the same even when I hot reload the app. Does this mean that they are the same widget but referenced multiple times? ... But in case of BuildContainerWidget the hashCodes change every time I hot reload the app. Why does this happen?
'''
import 'package:flutter/material.dart';
void main() {
runApp(const MyApps());
}
class MyApps extends StatelessWidget {
const MyApps({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Test',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
final List<Widget> widgets = const [
BuildSizedBoxWidget(),
BuildSizedBoxWidget(),
BuildContainerWidget(),
BuildContainerWidget()
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('What is happening'),
),
body: Column(
children: widgets,
),
);
}
}
class BuildSizedBoxWidget extends StatelessWidget {
const BuildSizedBoxWidget({super.key});
#override
Widget build(BuildContext context) {
const Widget widget = SizedBox(height: 50, child: Text('test'));
print(widget.hashCode);
return widget;
}
}
class BuildContainerWidget extends StatelessWidget {
const BuildContainerWidget({super.key});
#override
Widget build(BuildContext context) {
Widget widget = Container(height: 50, color: Colors.red);
print(widget.hashCode);
return widget;
}
}
'''
The variables defined in the body of the build method will be re-initialized during SetState.
Variables specified by the const keyword are not initialized.
There are only three parameters required by SizeBox Widget, and all of them can be initialized.
But Container Widget contains many parameters that cannot be initialized. So Container cannot be specified with the const keyword
If you put them outside the body of the build method, the HasCode will not change
class BuildContainerWidget extends StatelessWidget {
BuildContainerWidget({super.key});
Widget widget = Container(key: Key('value'), height: 50, child: Text('test'));
#override
Widget build(BuildContext context) {
print(widget.hashCode);
return widget;
}
}

Why am i getting No MediaQuery widget ancestor found .error

I have checked some threads here on StackOverlow but they dont fix my problem.The suggestions are
Create a new Stateless/Stateful widget and pass it to the home parameter OR
Use the Builder widget and pass it to the home parameter.
which I already did.
This is my main.dart file
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
ScreenUtil.init(context,designSize: Size(360,640));
return MaterialApp(
title: 'Flutter Demo',
home: HomeScreen()
);
}
}
And this is home.dart file
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Row(
children: [
buildLeftColumn(),
SizedBox(
width: 20.w,
),
// buildRightColumn(),
],
)),
);
}
buildLeftColumn() {
return Container();
}
So. what am i doing wrong.Could you please help
If you like to use ScreenUtil.init(...) you can solve this issue calling it on HomeScreen widget(context).
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
ScreenUtil.init(context, designSize: Size(360, 640));
To use it before MaterialApp you can use ScreenUtilInit widget.
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(360, 640),
builder: (context, child) => MaterialApp(
title: 'Flutter Demo',
home: HomeScreen(),
),
);
}

CustomPaint designs one below another in Flutter

I am trying to build a website using flutter and I want it's background to have a wavy corner with a different color at one end. I need four blocks in this designs i.e. as you scroll you'll reveal new blocks, and the height of each block should be the same as the height of the screen.
So, I thought of using CustomPaint to do it so I started to implement it but I am getting around 500 lines of error messages and it's not working.
I'm new to flutter so I don't know if I've implemented everything the way it should be implemented. I've shared all the code down below:
main.dart
import 'package:flutter/material.dart';
import 'blocks/block1.dart';
import 'blocks/block2.dart';
import 'blocks/block3.dart';
import 'blocks/block4.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: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
BlockOne(),
//BlockTwo(),
//BlockThree(),
//BlockFour()
],
),
)
);
}
}
block1.dart - block 2, 3 and 4 are almost similar:
Note- I've not added the wavy pattern in this code below because this itself is not working
import 'package:flutter/material.dart';
class BlockOne extends StatefulWidget {
#override
_BlockOneState createState() => _BlockOneState();
}
class _BlockOneState extends State<BlockOne> {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 1024)
return Desktop();
else
return Mobile();
},
);
}
}
class Desktop extends StatefulWidget {
#override
_DesktopState createState() => _DesktopState();
}
class _DesktopState extends State<Desktop> {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Desktop'),
);
}
}
class Mobile extends StatefulWidget {
#override
_MobileState createState() => _MobileState();
}
class _MobileState extends State<Mobile> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomPaint(
size: const Size(double.infinity, double.infinity),
painter: MobilePainter(),
),
);
}
}
class MobilePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final height = size.height;
final width = size.width;
Paint paint = Paint();
paint.color = Colors.black;
Path background = Path();
background.addRect(Rect.fromLTRB(0, 0, width, height));
canvas.drawPath(background, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate != this;
}
}
Check the following. Here I am using a list view to show contents. In flutter it is important to understand the concept that constraints go down from parent to child and the size goes up from a child to parent.
Here is demo of how this can be achieved. dartpad demo
If the child doesn't define a size then in ListView you have to define the itemExtent.
import 'package:flutter/material.dart';
// import 'blocks/block1.dart';
// import 'blocks/block2.dart';
// import 'blocks/block3.dart';
// import 'blocks/block4.dart';
void main() {
runApp(ScrollableView());
}
class ScrollableView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
// child: Column(
shrinkWrap: true,
itemExtent: 200,
children: <Widget>[
BlockOne(),
Container(
color: Colors.black,
),
BlockOne(),
Text('Hello'),
BlockOne(),
Container(
color: Colors.black,
),
BlockOne(),
//BlockTwo(),
//BlockThree(),
//BlockFour()
],
// ),
));
}
}
class BlockOne extends StatefulWidget {
#override
_BlockOneState createState() => _BlockOneState();
}
class _BlockOneState extends State<BlockOne> {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// if (constraints.maxWidth > 1024)
// return Desktop();
// else
return Mobile();
},
);
}
}
class Desktop extends StatefulWidget {
#override
_DesktopState createState() => _DesktopState();
}
class _DesktopState extends State<Desktop> {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Desktop'),
);
}
}
class Mobile extends StatefulWidget {
#override
_MobileState createState() => _MobileState();
}
class _MobileState extends State<Mobile> {
#override
Widget build(BuildContext context) {
return CustomPaint(
size: const Size(double.infinity, double.infinity),
painter: MobilePainter(),
);
}
}
class MobilePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final height = size.height;
final width = size.width;
Paint paint = Paint();
paint.color = Colors.green;
Path background = Path();
background.addRect(Rect.fromLTRB(0, 0, width, height));
canvas.drawPath(background, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate != this;
}
}

Getting error when I use Media Query in flutter web?

Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size.width;
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ( !kisweb || screenSize <= 600) ? MyHomePageMobile() : MyHomePage(),
),
);
}
I am getting this error and I have no idea how to solve it
You can't declare the MediaQuery above the Material app and it needs context. So the proper declaration should be like this.
class Main extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Test(),
);
}
}
class Test extends StatelessWidget {
const Test({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size.width;
return Container(
child: Scaffold(
body: ( !kisweb || screenSize <= 600) ? MyHomePageMobile() : MyHomePage(),
),
);
}
}

Flutter Provider: Why does this error happens when calling Provider.of<Widget>(context) like tihis:

I'm new to flutter and I'm testing Provider and can't figure out why doing this works (by work i mean it shows a list in the appbar):
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
builder: (context)=> Data(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: CustomText(),
),
),
),
);
}
}
With a CustomText class that does practically nothing:
class CustomText extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Text(Provider.of<Data>(context).texts.tostring());
}
}
But this other thing throws a - Could not find the correct Provider above this MyApp Widget - Error:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
builder: (context)=> Data(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(Provider.of<Data>(context).texts.toString()),
),
),
),
);
}
}
The Data class is :
class Data with ChangeNotifier{
List<String> _texts = ['Text 1', 'Text 2', 'Text 3', 'Text 4',];
get texts {
return _texts;
}
void deleteText(int index){
this._texts.removeAt(index);
notifyListeners();
}
void addText(String text){
this._texts.add(text);
notifyListeners();
}
}
I just can not see what is the difference or why that matters. Shouldn't that code be equivalent? Any insight will be much appreciated.
The difference is that in the CustomText case, context is from its parent widget which is MyApp, whereas in the second case, context is from MyApp's parent. Since your Provider is implemented inside MyApp, it cannot find the provider if you use MyApp's parent's context (the second case)