How to make the application responsive using Stack and Positoned - Flutter - flutter

How to use Stack and Positioned to add a shape in the SafeArea, I tried to change the color of the AppBar and connect to the shape and add mediaQuery, but still not on every screen it will be properly connected. So how to get a svg photo on the entire surface of SafeArea, and to make it responsive without using appbar, is it necessary to get the effect like in the picture below?(the code gives the effect as in the picture, but it is not responsive and consists of two parts, and I would like one part and get responsive)
Any help very much appreciated.
class Shape extends StatelessWidget {
static Route route() {
return MaterialPageRoute<void>(builder: (_) => Shape());
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
elevation: 0,
actions: [],
),
body: _profilePage(context),
);
}
Widget _profilePage(BuildContext context) {
return SafeArea(
child: Align(
child: Center(
child: Stack(
children: <Widget>[
Positioned(
width: MediaQuery.of(context).size.width * 1,
height: MediaQuery.of(context).size.height,
bottom: MediaQuery.of(context).size.width * 0.6,
child: _curved(context),
),
],
),
),
),
);
}
Widget _curved(BuildContext context) {
return SvgPicture.asset(
'assets/images/shape_purple.svg',
color: Colors.blue,
allowDrawingOutsideViewBox: true,
);
}

Use FitteBox Widget instead
FittedBox(
child: Image.asset('assets/images/background.png'),
fit: BoxFit.fill,
// decoration: BoxDecoration(
// color: Colors.white),
),

Related

How can I make my app more responsive and handle different screen size?

Any help is appreciated! I've been to trying to figure out why my app doesn't handle different screen size.. I added width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height inside a container to make it more responsive, however, still not responsive. only it's responsive on ios simulator with the overflow error.. not on android simulator. What am I doing wrong?!
How can I make my app handle different screen size both on android and ios simulator?
home: Builder(builder: (context) {
return DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.deepOrange.shade200,
title: Text('Weather App', style: GoogleFonts.roboto()),
bottom:
tabs: [
Tab(
icon: FaIcon(
FontAwesomeIcons.solidSun,
color: Colors.orange,
),
),
Tab(
icon: Icon(
Icons.favorite,
color: Colors.pink,
)),
]),
),
body: Stack(children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.deepOrange.shade400, Colors.white],
),
),
child: TabBarView(
children: <Widget>[
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ForecastWeather(),
),
);
},
child: Card(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.only(top: 120.0),
child: Weather(weather: weatherData),
),
),
I think this is main.dart file could u try like this?
class AppName extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
Go home_page.dart and do there.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container( //this container can be your background.dart or whatever u want
height: size.height * 1,
width: size.width * 1,
);
}
}
like this.
It depends on your goal. Do you want different behavior on different Screen sizes or do you just want to arrange your Elements to get a lean experience on all devices?
If it is the second, then it's not practical to use MediaQueries to measure it all by yourself. Instead try to learn and use flutters layouting.
Here is a Basics Codelab for that:
https://docs.flutter.dev/codelabs/layout-basics
For basic responsive Design without fancy stuff like different layouts for Tables or Foldables, it's way easier to handle it with Layout Design.
I am using flutter_screenutil in my application.
Initallize it with your design size.
Widget build(BuildContext context) {
ScreenUtil.init(
BoxConstraints(
maxWidth: MediaQuery.of(context).size.width,
maxHeight: MediaQuery.of(context).size.height),
designSize: Size(360, 690),
minTextAdapt: true,
orientation: Orientation.portrait);
return Scaffold();
}
And use like this:
Container(
width: 50.w,
height:200.h
)
This turns 50 dp in your design responsive. Check out their documentation for more information.

ListView in LimitedBox vs ConstrainedBox

While trying to figure out how to limit the size of a ListView, I saw examples in Stackoverflow suggesting that doing something like placing the ListView in a LimitedBox and setting the maxHeight of the Limited box and the shrinkwrap of the ListView, would accomplish it. However, the ListView would still grow past the maxheight to accommodate the size of the children in the ListView.
Eventually, I tried an example using ConstrainedBox and setting it's maxHeight and the Shrinkwrap of the ListView; doing this seemed to accomplish the intended result of limiting the Listview height to the maxHeight setting.
Can someone help me understand why LimitedBox maxHeight does not work in this case, when ConstrainedBox does?
Here is an example of the LimitedBox that is not constraining itself to the maxHeight:
Widget _getPopupCard(){
return Hero(
tag: 0,
createRectTween: (begin,end){
return MaterialRectCenterArcTween(begin: begin, end: end);
},
child: Container(
child:Padding(
padding: EdgeInsets.all(14),
child: Material(
borderRadius: BorderRadius.circular(18),
color: Colors.white,
child: LimitedBox(
maxHeight: 300,
child: ListView(
shrinkWrap: true,
children: _getTiles(),
),
),
),
),
),
);
}
Here is an example of the ConstrainedBox version that does limit itself to the maxHeight:
Widget _getPopupCard(){
return Hero(
tag: 0,
createRectTween: (begin,end){
return MaterialRectCenterArcTween(begin: begin, end: end);
},
child: Container(
child:Padding(
padding: EdgeInsets.all(14),
child: Material(
borderRadius: BorderRadius.circular(18),
color: Colors.white,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 300.0),
child: ListView(
shrinkWrap: true,
children: _getTiles(),
),
),
),
),
),
);
}
https://api.flutter-io.cn/flutter/widgets/LimitedBox-class.html
LimitedBox : A box that limits its size only when it's unconstrained.
ConstrainedBox : which applies its constraints in all cases, not just when the incoming constraints are unbounded.
LimitedBox can only works when parent is unconstrainted and flutter cannot render infinity width/heigh, so LimitedBox must both provide MaxWidth and MaxHeight
You can wrap UnconstrainedBox to make LimitedBox works, but you really don't need this... see: https://www.woolha.com/tutorials/flutter-using-limitedbox-widget-examples
https://dartpad.dev/2cfc997a7984bf848687f209cba917ce?null_safety=true
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'LimitedBox',
home: Scaffold(
appBar: AppBar(
title:
Text('LimitedBox can only work when parent is unconstrainted')),
body: Center(
child: UnconstrainedBox( // wrap UnconstrainedBox to make LimitedBox works, but you really don't need this... https://www.woolha.com/tutorials/flutter-using-limitedbox-widget-examples
child: _getPopupCard(),
),
),
),
);
}
}
Widget _getPopupCard() {
return LimitedBox(
maxHeight: 300.0,
maxWidth: 300.0,
child: ListView(
shrinkWrap: true,
children: _getTiles(),
),
);
}
List<Widget> _getTiles() {
return List.generate(100, (int i) => i).map((int i) {
return Container(
color: i % 2 == 0 ? Colors.red : Colors.green,
child: Center(child: Text("$i")),
);
}).toList();
}
update:
Is Container the constrained parent that is overriding the LimitedBox even though I didn't set height or width on the Container?
I search this question just because I'm reading this guide: https://flutter.dev/docs/development/ui/layout/constraints
Container's layout behavior is little complex.
About this, I've run code below , seems flutter will give Container a default Constraints. The Container source tells something, maybe you should get some info from the source.
runApp(
MaterialApp(
home: UnconstrainedBox(
child: LayoutBuilder(
builder: (ctx, Constraints constraints) {
print(ctx);
print(constraints);//BoxConstraints(unconstrained)
return Text("hello world");
},
),
),
),
);
runApp(
MaterialApp(
home: Container(
color: Colors.white,
child: LayoutBuilder(
builder: (ctx, constraints) {
print(ctx);
print(constraints); // BoxConstraints(w=500.0, h=296.0)
return Text("hello world333333");
},
),
),
),
);

AnimatedContainer - How do I expand only one and the rest stay below?

So there's something I'm working on and I want to have a list of these "capsules" (rounded rectangle containers). When the user taps on any given one of them, it expands to the full screen, while the rest stay on a lower layer and don't do anything.
I'm using AnimatedContainer and GestureDetector to change their state. When there's only one, it works perfectly for what I want to do. Meanwhile, as soon as I add more in a Column, because it's a single Widget I coded inside a GestureDetector with a single boolean, they all open at the same time. And I understand that even if I code them separately, it will basically just push the surrounding ones out of the way, not open above them. How would I deal with this?
I tried searching this and couldn't find anything helpful. Hopefully the answer to this will help future projects too.
bool chatCapsuleTapped = false;
bool hasFullSize = false;
#override
Widget build(BuildContext context) {
Widget _chatCapsuleAnimation() {
return GestureDetector(
onTap: () {
setState(() {
chatCapsuleTapped = !chatCapsuleTapped;
hasFullSize = true;
});
},
child: AnimatedContainer(
width: !chatCapsuleTapped ? 350 : MediaQuery.of(context).size.width,
height: !chatCapsuleTapped ? 75 : MediaQuery.of(context).size.height,
//color: !chatCapsuleTapped ? Colors.grey.withOpacity(1) : Colors.grey,
decoration: BoxDecoration(
color: !chatCapsuleTapped ? Colors.grey.shade500 : Colors.grey.shade300,
borderRadius: !chatCapsuleTapped ? BorderRadius.circular(40) : BorderRadius.circular(0),
),
child: !chatCapsuleTapped ? Container(child: Container(),) : Container(),
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
),
);
}
return Scaffold(
body: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_chatCapsuleAnimation(),
],
),
),
);
}
} ```
You can use Hero:
Place each widget inside a Hero widget, assign it a tag based on the index.
Then have a Full-Screen page, which contains the bigger version of the widget, but with the same tag as of the tapped item.
Sample Grabbed from here, you can paste it in DartPad
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Basic Hero Animation'),
),
body: SingleChildScrollView(
child: Column(
children: List<Widget>.generate(5, (index) {
return InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Full-Screen Page'),
),
body: Container(
child: Hero(
// TAG should be same as the tapped item's index
tag: index.toString(),
child: SizedBox(
child: Container(
color: Colors.grey[(index + 1) * 100]),
),
),
),
);
},
),
);
},
child: Hero(
// Assigning tag of item as its index in the list
tag: index.toString(),
child: Container(
height: 200, color: Colors.grey[(index + 1) * 100]),
));
}))),
);
}
}
I've put the destination page within the scope of the main file for simplicity, but you can make a seperate Widget and accept index as parameter for the Bigger Hero's tag

Flutter Transform.scale for child widgets not scaling proportionately, why?

I have a basic flag widget, with code here:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Flag(),
),
);
}
}
class Flag extends StatelessWidget {
#override
Widget build(BuildContext context) {
var scaleFactor = 1.0;
final flag = Transform.scale(
scale: scaleFactor,
child: AspectRatio(
aspectRatio: 5 / 3,
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 2, color: Colors.black),
),
child: Center(
child: Container(
height: 400,
width: 150,
color: Colors.purple,
),
),
),
),
);
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) => flag,
);
}
}
It looks like this:
If I change scaleFactor to 0.5, it scales as expected:
But if I rotate my emulator to landscape, the proportions are off:
If I make the stripe proportional to the landscape view (e.g. change the stripe width to 250), it gets too big in portrait mode:
How do I ensure the purple stripe takes up the same proportion of space regardless of device size?
The flag is going to get way more complicated so I don't want to use MediaQuery.of(context).size to get the device width and calculate a percentage of that for every single child widget...
Do I need to tell my widget its "canonical" size? Do I need to pass a scaleFactor to every child widget?
Any ideas very appreciated :)
So, I see two ways to solve this, depending on what your eventual goal is. I think the first one is probably what you actually want.
1) Use a Row and an Expanded widget for each part of the flag, with a flex of one. The AspectRatio will keep the aspect ratio fixed, so the proportions should remain the same for the containers. Since the default flex factor is one anyhow, you can also just leave that out. If you need to the white parts to be transparent, just give Colors.transparent as the color.
class Flag extends StatelessWidget {
#override
Widget build(BuildContext context) {
var scaleFactor = 1.0;
final flag = Transform.scale(
scale: scaleFactor,
child: AspectRatio(
aspectRatio: 5 / 3,
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 2, color: Colors.black),
),
child: Row(
children: <Widget>[
Expanded(
flex:1,
child: Container(
color: Colors.white,
),
),
Expanded(
flex:1,
child: Container(
color: Colors.purple,
),
),
Expanded(
flex:1,
child: Container(
color: Colors.white,
),
),
],
),
),
),
);
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) => flag,
);
}
}
2) You could just scale the contents of the center with a Transform widget, but that will squish any content you put inside it, so that's probably not what you want.
class Flag extends StatelessWidget {
#override
Widget build(BuildContext context) {
var scaleFactor = 1.0;
final flag = Transform.scale(
scale: scaleFactor,
child: AspectRatio(
aspectRatio: 5 / 3,
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 2, color: Colors.black),
),
child: Center(
child: Transform(
transform: Matrix4.diagonal3Values(0.33, 1.0, 1.0),
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.purple,
),
),
),
),
),
);
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) => flag,
);
}
}

flutter statfulWidget refresh twice

return MaterialApp(
home: Scaffold(
body: RefreshIndicator(
key: _globalKey,
onRefresh: refresh,
child: Sub(),
),
),
);
there is simple refreshindicator and sub Widget
class Sub extends StatefulWidget {
#override
_SubState createState() => _SubState();
}
class _SubState extends State<Sub> {
#override
Widget build(BuildContext context) {
print('sub');
return Container(
color: Colors.amber,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
height: 300,
color: Colors.blue,
),
Container(
height: 300,
color: Colors.red,
),
Container(
height: 300,
color: Colors.green,
),
],
),
),
);
}
}
Sub Widget print "sub" when it is built
the point is when i touch or just slightly drag page(not enough call refreshindicator) sub widget is rebuilt TWICE. i don't know why and i don't know how can i slove.
(statelesswidget can solve that problem but i need to use statefulwidget)
It depends what you want to achieve without re-building but alternate I used was for flutter charts. Create a object Sub sub1=Sub(); , somewhere outside build() or intiState() , then this widget will not rebuild again and values will be same.