Create device height background element - flutter

I am very new to flutter so please be fair.
I would like to create the following.
Layout
Background which is always the entire screen-size (includes multiple stacked images)
Content (adapts to the the normal app ui behaviours -> ui element like keyboard)
I can't seem to figure out how to create this background element which should not resize when the keyboard is pulled out.
I have this and I hope someone could give me a hand.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
var insetHeight = MediaQuery.of(context).viewInsets.bottom;
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Stack(
children: <Widget>[
Stack(
children: <Widget>[
Positioned(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height + insetHeight,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/background.png"),
fit: BoxFit.cover,
alignment: Alignment.bottomCenter,
),
)
),
),
],
),
SizedBox(
width: 370,
child: SingleChildScrollView(
physics: PageScrollPhysics(), // dont really need this
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: Text("Login to",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Opensans",
fontSize: 30,
letterSpacing: .6,
color: Colors.black45,
fontWeight: FontWeight.normal
)
),
),
Card(
child: Padding(padding: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0), child: LoginForm()),
)
],
),
)
)
],
),
);
}
}
I have tried to use the MediaQuery.of(context).viewInsets.bottom; but it's always returning 0.
I am not sure how to solve this.

The Scaffold rebuilds its body when keyboard is visible.
So move your background widget outside the Scaffold.
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
SizedBox.expand(
child: Image.asset(
"assets/images/background.png",
fit: BoxFit.cover,
),
),
Scaffold(
backgroundColor: Colors.transparent, //Should be transparent
body: Center(
child: TextField(),
),
),
],
);
}
}

For background, Try following:
Create a variable Size _screensize in _MyHomePageState.
Override initState in _MyHomePageState and do following:
#override
void initState() {
super.initState();
_screenSize ??= MediaQuery.of(context).size;`
}
Use this _screenSize as width and height for Positioned as below:
Positioned(
width: _screenSize.width,
height: _screenSize.height,
),
Add other normal widgets as usual.
Summary for background:
step 1: creating a private reference for size to use.
step 2: updating the private size variable only when it is null, which is only when the widget state is initialized the first time.
step 3: using private size as height and width for the background.

Related

Align child widgets to a positioned child widget

I need to position a child widget (w1) at a relative position to its parent (in the example see the white box at 30%), and to position two other child widgets (w2, w3) to the right and left.
The positioned child (w1) is the anchor and should remain in 30%, regardless of w2 and w3 width, while the other child widgets should positioned accordingly.
Here's what I'm trying to achieve:
I tried to use Stack widget as the parent
The problem is I can't place the position child at 30%, as it only excepts const values
I also tried to placed them in Container with FractionalOffset - it works for single children, but I can't add another children to this Container.
Container(
height: 50,
color: Colors.blue[200],
alignment: const FractionalOffset(0.3, 0),
child: Text("50",style: const TextStyle(fontSize: 16, color: Colors.black))),
You can use CompositedTransformTarget widget.
class TestF133 extends StatelessWidget {
const TestF133({super.key});
#override
Widget build(BuildContext context) {
final LayerLink link = LayerLink();
return Scaffold(
backgroundColor: Colors.deepPurple,
body: Stack(
children: [
Align(
alignment: Alignment(.3, 0),
child: CompositedTransformTarget(
link: link,
child: Container(
height: 50,
width: 50, // you can handle theses
color: Colors.white,
),
),
),
CompositedTransformFollower(
link: link,
followerAnchor: Alignment.centerLeft,
targetAnchor: Alignment.centerRight,
child: Text("dynamic right Text"),
),
CompositedTransformFollower(
link: link,
followerAnchor: Alignment.centerRight,
targetAnchor: Alignment.centerLeft,
child: Text("dynamic left Text"),
)
],
),
);
}
}
I hope I got your question right. So, a quick solution would be:
class MyBar extends StatelessWidget {
const MyBar({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return Container(
color: Colors.blue,
child: Row(
children: [
SizedBox(
width: width * 3 / 10,
child: const Align(
alignment: Alignment.centerRight,
child: Text('left'),
),
),
Container(
height: 50,
color: Colors.lime.shade100,
margin: const EdgeInsets.symmetric(horizontal: 10),
child: const Text("50",
style: TextStyle(fontSize: 16, color: Colors.black)),
),
const Expanded(child: Text('right')),
],
),
);
}
}
For more accurate result, I think you can find the width of the handler and subtract the half of it from the width of the SizedBox (or something like that).
You can also use Stack with position with this approach.

How to solve stack widget problem in flutter pageview?

Im developing a quotes app as a beginner in flutter.I had used a stack widget to use both image and text in the center of my screen.And added "pageview widget" to have multiple page in my app.But the problem im facing is,whenever im trying to swipe the page,image get swiped but the text stayed the same.I mean the fist page "Text", still showing in the middle of 2nd page.Moreover i cant drag to the next page while touching the letter,its only being done via touching above or under the letter.I just wants to have a simple multipage app with text and image. How can i solve this Problem?
class OverviewScreen extends StatefulWidget {
const OverviewScreen({key, Key,}) : super(key: key);
#override
_OverviewScreenState createState() => _OverviewScreenState();
}
class _OverviewScreenState extends State<OverviewScreen> {
PageController pageController =PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: Text("Hu R Rehman",
style: TextStyle(fontFamily: "MonteCarlo"),),
centerTitle: true,
leading: Icon(Icons.menu),
shape:RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(16))
),
backgroundColor: Colors.transparent,
elevation: 0,
),
body:Center(
child: Stack(
alignment: Alignment.center,
children: [
PageView(
controller: pageController,
children: [
Image(
image:AssetImage('Image/soc1.jpg'),
fit:BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
p1()
],
),
const Text('Text'
,style: TextStyle(fontSize: 31.0,
fontFamily:"MonteCarlo",
color: Colors.white,
fontWeight: FontWeight.w700 )
),
],
),
)
),
);
}
}
Here p1() is the second page
class p1 extends StatefulWidget {
#override
_p1State createState() => _p1State();
}
class _p1State extends State<p1> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.blueGrey,
),
);
}
}
If you take a look at widget tree, There are two widgets PageView and Text placed as Stack Children, and according to widget priority render Text widget will be at the top. Also, it is independent from PageView, and there will be no effect on this Text widget by changing Pageview.
let say you wish to part it on 1st child of PageView, you can move it there by wrapping with stack like this
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
PageView(
controller: pageController,
children: [
Stack(
alignment: Alignment.center,
children: [
Image(
image: AssetImage('Image/soc1.jpg'),
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
const Text(
'Text',
style: TextStyle(
fontSize: 31.0,
fontFamily: "MonteCarlo",
color: Colors.white,
fontWeight: FontWeight.w700,
),
),
],
),
p1()
],
),
],
),
)
Does it solve your issue?
Try putting Stack inside PageView children.
PageView(controller: pageController, children: [
Stack(
alignment: Alignment.center,
children: [
Image(
image: AssetImage('Image/soc1.jpg'),
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
const Text('Text',
style: TextStyle(
fontSize: 31.0,
fontFamily: "MonteCarlo",
color: Colors.white,
fontWeight: FontWeight.w700)),
],
),
p1(),
],
);

VerticalCardPager not taking full width

When using the VerticalCardPager on a page on its own, it works with no problems.
However, when using it beneath other widgets, the cards shrink in size. I have tried wrapping within a SingleChildScrollView to extend the page but this does not work with the Expanded/Flexible widget.
HomeScreen
import 'package:club2_app/widgets/vertical_card.dart';
import 'package:flutter/material.dart';
import '../widgets/news_card.dart';
import '../constants.dart';
import '../widgets/app_bar.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: buildAppBar(),
body: SafeArea(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 10, top: 15),
child: Text(
"News",
style: TextStyle(
color: kPrimaryLightColor,
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
),
],
), // 'News'
NewsCard(),
Row(
children: [
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(
'Posts',
style: TextStyle(
color: kPrimaryLightColor,
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
),
],
), // 'Posts'
Expanded(
child: Container(
padding: EdgeInsets.all(5.0),
width: double.infinity,
child: VerticalCard(),
),
)
],
),
),
);
}
}
VerticalCardWidget
import 'package:flutter/material.dart';
import 'package:vertical_card_pager/vertical_card_pager.dart';
class VerticalCard extends StatefulWidget {
#override
_VerticalCardState createState() => _VerticalCardState();
}
final List<String> titles = [
"How to Manage Injuries",
"Is Football Getting Faster?",
"Improving Match Fitness",
"Expected Goals: The Over and Under Achievers",
];
final List<Widget> images = [
Hero(
tag: "News 1",
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Container(
width: double.infinity,
child: Image.asset(
"assets/images/Mbappe.jpeg",
fit: BoxFit.cover,
),
),
),
),
Hero(
tag: "News 2",
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Image.asset(
"assets/images/Mbappe.jpeg",
fit: BoxFit.cover,
),
),
),
Hero(
tag: "News 3",
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Image.asset(
"assets/images/Mbappe.jpeg",
fit: BoxFit.cover,
),
),
),
Hero(
tag: "News 4",
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Image.asset(
"assets/images/Mbappe.jpeg",
fit: BoxFit.cover,
),
),
),
];
class _VerticalCardState extends State<VerticalCard> {
#override
Widget build(BuildContext context) {
return VerticalCardPager(
titles: titles,
images: images,
onPageChanged: (page) {
// print(page);
},
);
}
}
I'm afraid it is a limitation of the VerticalCardPager.
The width and height of the cards inside a VerticalCardPager are defined based on the maxHeight of the Parent Widget:
double getWidth(maxHeight, i) {
double cardMaxWidth = maxHeight / 2;
return cardMaxWidth - 60 * (i - 2).abs();
}
double getHeight(maxHeight, i) {
double cardMaxHeight = maxHeight / 2;
if (i == 2) {
return cardMaxHeight;
} else if (i == 0 || i == 4) {
return cardMaxHeight - cardMaxHeight * (4 / 5) - 10;
} else
return cardMaxHeight - cardMaxHeight * (4 / 5);
}
}
For i == 2, the middle Card, the width and height are both equal to maxHeight / 2.
So, if you want the Cards to have a different aspect ratio and fill the width of the screen, you will have to modify the code of the VerticalCardPager.
However, if you want to keep the same aspect ratio and expand out of the screen. You can use a SingleChildScrollView as you mentioned. Here you go:
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Vertical Pager')),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
[...]
SizedBox(
height: MediaQuery.of(context).size.height,
child: Container(
padding: EdgeInsets.all(5.0),
width: double.infinity,
child: VerticalCard(),
),
)
],
),
),
),
);
}
}
However, the scrolling behavior might be a little bit chaotic, or even impossible on some devices.
try to wrap Your column into container and set its width to "double.infinity"

Fade Image.asset when SingleChildScrollView scrolls

I have an image.asset at the top of a Column inside a SingleChildScrollView
I'm trying to achieve that when the users scrolls the scrollView, the image will fade out.
I've tried to to it using the controller property of the scrollView, but I couldn't achieve the opacity change.
Does anyone has an idea what is the most efficient way to do that?
Thank you!
Current Code:
Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Column(
children: [
Stack(
children: [
Container(
height: _imageTopPosition + _imageHeight,
color: Colors.blue[100],
),
Positioned(
top: _customShapeTopPosition,
child: MyCustomShape(
size: Size(_screenSize.width, _customShapeHeight),
),
),
Positioned(
top: _imageTopPosition,
right: _imageRightPosition,
child: Image.asset( //The image I would like to fade on scroll
'assets/images/image.png',
width: _imageWidth,
height: _imageHeight,
),
),
],
),
Padding(
padding: EdgeInsets.only(top: _screenSize.height * 0.05),
),
Text('Some Text'),
],
),
),
);
Just create an animation controller and update its value according to the scroll controller
full working example:
import 'package:flutter/material.dart';
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> with SingleTickerProviderStateMixin{
var _controller=ScrollController();
AnimationController animation;
#override
void initState() {
super.initState();
animation=AnimationController(vsync:this);
_controller.addListener(() {
animation.value=1-_controller.offset/_controller.position.maxScrollExtent;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:Center(
child:Container(
width: 300,
height: 300,
child: SingleChildScrollView(
controller: _controller,
child:FadeTransition(
opacity: animation,
child:Container(
width: 300,
height: 600,
color: Colors.red,
)
)
),
),
)
);
}
}

I am getting an error, The method '*' was called on null

I have created a button class that extends a stateless widget for creating a customized button widget. I have used this button to create buttons in a class and it worked perfectly fine. But, when i tried to create a button in an another class, using the same button widget, i got an error,'The method '*' was called on null and RenderFlex overflowed' which i could not get why is it appearing. Can somebody help me, what and where did i do the blunder.
This one is the button widget
import 'package:flutter/material.dart';
import 'package:group_project/ui/size_config.dart';
import 'package:group_project/widgets/responsive_widget.dart';
// Button Widget
class Button extends StatelessWidget {
final IconData icon;
final String btnName;
final double height;
final double width;
final Color buttonColor;
final Color iconColor;
final double iconSize;
final Color textColor;
final double btnTextSize;
Border border;
BorderRadius btnBorderRadius;
MainAxisAlignment mainAxisAlignment;
CrossAxisAlignment crossAxisAlignment;
Button({
#required this.icon,
#required this.btnName,
this.height,
this.width,
this.buttonColor,
this.iconColor,
this.textColor,
this.border,
this.mainAxisAlignment,
this.crossAxisAlignment,
this.btnTextSize,
this.iconSize,
this.btnBorderRadius,
});
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: height,
width: width,
decoration: BoxDecoration(
color: buttonColor,
border: border,
borderRadius: btnBorderRadius,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: mainAxisAlignment,
children: [
Text(
btnName,
style: TextStyle(
fontSize: btnTextSize * SizeConfig.textMultiplier,
color: textColor,
),
),
Icon(
icon,
color: iconColor,
size: iconSize * SizeConfig.imageSizeMultiplier,
),
],
),
),
);
}
}
This is the class where I got the error.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:group_project/ui/size_config.dart';
import 'package:group_project/widgets/widgets.dart';
// import 'package:group_project/data/data.dart';
// import 'package:group_project/widgets/product_carousel_widget.dart';
class ProductsPage extends StatefulWidget {
#override
_ProductsPageState createState() => _ProductsPageState();
}
class _ProductsPageState extends State<ProductsPage> {
Size size;
bool visible = true;
void isVisible() {
if (visible == true) {
visible = false;
} else {
visible = true;
}
}
#override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Color(0xfff0f0f0),
body: ListView(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
Container(
height: 233.33 * SizeConfig.heightMultiplier,
width: double.infinity,
color: Colors.blue,
child: Image(
image: AssetImage('images/jacket.jpg'),
fit: BoxFit.cover,
),
),
Stack(
children: [
// Products description
Visibility(
visible: visible,
child: Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Container(
padding: EdgeInsets.fromLTRB(10.0, 40.0, 10.0, 10.0),
height: 400,
width: double.infinity,
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20.0),
// border: Border.all(color: Colors.blue),
color: Colors.blue,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Iphone Pro Max'
),
Text(
'Rs. 125000'
),
],
),
SizedBox(
height: 20.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'(Used)'
),
Text(
'Condition: Good'
),
],
),
SizedBox(
height: 20.0,
),
Text(
'This is iphone 11 pro max, 64 GB variant. The size of the mobile phone is 6.5 inches. Released 2019, September ',
),
SizedBox(
height: 20.0,
),
Row(
children: [
// This is the button where exactly I am getting the error
Button(
height: 25 * SizeConfig.heightMultiplier,
width: 80,
icon: Icons.shopping_cart,
btnName: 'Add',
),
],
),
],
),
),
),
),
FlatButton(
padding: EdgeInsets.all(0),
onPressed: () {
setState(() {
isVisible();
});
},
child: Button(
icon: EvaIcons.chevronDown,
btnName: 'Show Description',
height: 25.0 * SizeConfig.heightMultiplier,
width: double.infinity,
buttonColor: Colors.blue,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
btnTextSize: 8.0,
iconSize: 20,
textColor: Colors.white,
iconColor: Colors.white,
// btnBorderRadius: BorderRadius.circular(0),
),
),
],
),
],
),
],
),
],
),
);
}
}
Wrap it with Expanded Widget when using inside a Row or Column
Expanded(
child: Button(
height: 25 * SizeConfig.heightMultiplier,
width: 80,
icon: Icons.shopping_cart,
btnName: 'Add',
),
),
Your Row has no defined height or width. Try wrapping your row in an Expanded widget.
This also why you're getting the null reference exception. You're sizeConfig (Which I assume is based off of MediaQuery.of(context).size) Gets it's context from it's parent widget and since a Row widget doesn't define (It is a flexible widget) height or width, it will return null for size. Expanded will tell the Row to set its size to all available space.
#override
Widget build(BuildContext context) {
return SingleChildScrollView(child: Container(...));}
surround your code with SingleChildScrollView that's why render flex error is showing
for the '*' is called on null error click on the run button at the bottom of android studio and look at the log and click the link with ProductPage.dart and it will take you to the line where the error happens
Try giving it a width or wrapping with a Expanded
It is given infinite width because MediaQuery is always given the context of it's parent, and in this scenario it gets it from Row widget which doesn't have a defined width. Neither does a Column widget have a defined height. That's why it's giving the error.
Wrapping it with Expanded widget will make the Row take all of free space.