Flutter position fixed equivalent - dart

Is it possible to fix an object in the screen that stays fixed regardless of scrolling?
Something similar to CSS position fixed.

You can absolutely position a child of a Stack widget using the Positioned widget.
The minimal example below places the red box above the list view, by placing the child in a Positioned widget after the ListView in the Stack's children.
List<String> todos = [...];
return new Stack(
children: <Widget>[
new ListView(
children: todos
.map((todo) => new ListTile(title: new Text(todo)))
.toList(),
),
new Positioned(
left: 30.0,
top: 30.0,
child: new Container(
width: 100.0,
height: 80.0,
decoration: new BoxDecoration(color: Colors.red),
child: new Text('hello'),
)
),
],
);
And here it is inside of a Scaffold body. If you add more items you'll find that the list scrolls without moving the red box.

You could use Positioned widget in a Stack Widget with AspectRatio widget and use the % distance like the below code.
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; //get the screen size
List<String> todos = [...];
//the below if to get the aspect ratio of the screen i am using the app only in landscape
//if you need to use it in portrait you should add the sizes below
if((size.width / size.height) > 1.76){
aspect = 16 / 9;
}else if((size.width / size.height) < 1.77 && (size.width / size.height) >= 1.6){
aspect = 16 / 10;
}else{
aspect = 4 /3;
}
return new Scaffold(
body: new Center(
//layoutBuilder i can use the constraints to get the width and height of the screen
child: new LayoutBuilder(builder: (context, constraints) {
return new AspectRatio(
aspectRatio: aspect,
child: new Column(
children: <Widget>[
new ListView(
children: todos
.map((todo) => new ListTile(title: new Text(todo)))
.toList(),
),
new Positioned(
//constraints.biggest.height to get the height
// * .05 to put the position top: 5%
top: constraints.biggest.height * .05,
left: constraints.biggest.width * .30,
child: new Container(
width: 100.0,
height: 80.0,
decoration: new BoxDecoration(color: Colors.red),
child: new Text('hello'),
),
),
],
),
),
}),
),
);
}
}
Hope it will help you....

I prefer this solution:
Stack(
children: [
Column(
children: [
Container(...),
Container(...),
]
),
Positioned(top:50, child: Card(...))
]
)

Widget build(BuildContext context) {
return Column(
children: [
Container(
// covers 20% of total height
height: 200,
child: Stack(
children: [
Container(
height: 300,
decoration: BoxDecoration(
color: Colors.red,
),
),
Positioned(
bottom: 0,
left: 0,
width: 100,
height: 100,
child: Container(
height: 54,
decoration: BoxDecoration(
color: Colors.blue,
),
),
)
],
),
),
],
);
}
Adding a width and height to your Positioned widget will make it appear if you want to use top/left/bottom/right

Related

List tile with vertical line and image

I am trying to create a listview with an image and vertical line at the start of the list tile. I will try to explain with an image.
I have tried using a stack with a container for the vertical line, and then an image right after, but it didn't work. I also tried adding a Position.fill to the vertical line, which also didn't work.
Row(
children: <Widget>[
Stack(
children: <Widget>[
new Image(image: new AssetImage("assets/img/airplane.png")),
Positioned.fill(
child: Container(
height: 1.0,
width: 3.0,
color: Colors.green,
margin: const EdgeInsets.only(left: 30.0, right: 10.0),
),
),
],
)
],
),
This is what i am trying to achieve.
An example of an app on the store that does what I am trying to achieve:
Here an example:
class MainPageState extends State<MainPage> {
//State must have "build" => return Widget
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stack(alignment: const Alignment(0.0, 0.0), children: <Widget>[
Container(
//Do you need to make Image as "Circle"
child: Image.asset('images/sanBernardo1.jpg',
width: 150.0, height: 150.0, fit: BoxFit.fill),
),
Positioned(
left: 50.0,
child: Container(
width: 12.0,
height: 100.0,
padding: const EdgeInsets.all(5.0),
decoration: BoxDecoration(color: Colors.red[400])),
)
]));
}
}
Hope this help.
The correct widget for this case is Stepper
https://api.flutter.dev/flutter/material/Stepper-class.html
So, you can create progress through a sequence of steps with a lot of built-in functionality.
Also, it could be useful take a look to Material style guide https://material.io/archive/guidelines/components/steppers.html#

Flutter UI: Absolute positioned element with fixed height and 100% width

In flutter, I'd like to have a Container with a fixed height and 100% width.
To accomplish this, I used:
Row(
children: <Widget>[
Flexible(
child: Container(
color: Colors.blue,
height: 40.0,
),
),
],
),
Now, I'd like to offset this row a few pixels offscreen. To accomplish this, I'm attempting to use:
Stack(
children: <Widget>[
Positioned(
left: -5.0,
child: Row(
children: <Widget>[
Flexible(
child: Container(
color: Colors.blue,
height: 40.0,
),
),
],
),
),
],
),
This gives me the error:
The following assertion was thrown during performLayout(): RenderFlex children have non-zero flex but incoming width constraints are unbounded.
How would I create this layout effect?
Try giving specific size of children widgets. Positioned widget can't have flexible size of children.
So I gave screen width to Positioned(parent widget) and height 40. And you just need to give width of each children in Row. If you want to give them some flexible relationship, try MainAxisAlignment property inside Row widget.
Here is my example.
Positioned(
width: MediaQuery.of(context).size.width,
height: 40.0,
left: -5.0,
child: Container(
color: Colors.grey,
child: Row(
children: <Widget>[
Container(
color: Colors.green,
width: MediaQuery.of(context).size.width / 3,
child: Center(
child: Text("Green")
),
),
Container(
color: Colors.pink,
width: MediaQuery.of(context).size.width / 3,
child: Center(child: Text("Pink"))
),
Container(
color: Colors.blue,
width: MediaQuery.of(context).size.width / 3,
child: Center(child: Text("Blue")),
)
],
),
),
),
If you have a scenario where you don't want to set a fixed width for your children, you can set left and right both to 0 to stretch the Positioned element to the full width:
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(child: Text('Previous'), onPressed: () => {}),
ElevatedButton(child: Text('Next'), onPressed: () => {}),
],
),
),
You can also achieve the same effect with Positioned.fill() (passing null or a value for properties you want to override. https://api.flutter.dev/flutter/widgets/Positioned/Positioned.fill.html
https://youtu.be/EgtPleVwxBQ?t=66
Positioned.fill(
top: null,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(child: Text('Previous'), onPressed: () => {}),
ElevatedButton(child: Text('Next'), onPressed: () => {}),
],
),
),

Anchor floating action button to Card

When creating a Card (for example using the code from the Docs) , how can I anchor a FAB to the Card (the green circle in the image below), like in this question for Android.
I saw a similar question for attaching a FAB to the AppBar, but the solution relies on the AppBar being a fixed height. When using a Card, the height isn't fixed ahead of time so the same solution can't be used.
You can place the FloatingActionButton in an Align widget and play with the heightFactor property.
For example:
class MyCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
SizedBox(height: 100.0, width: double.infinity),
Align(
alignment: Alignment(0.8, -1.0),
heightFactor: 0.5,
child: FloatingActionButton(
onPressed: null,
child: Icon(Icons.add),
),
)
],
),
);
}
}
Correct solution for anchor FAB.
Another solution using stack and container. FAB's place is based on its sibling Container widget's size and clicks/taps work properly.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: MyWidget(),
),
);
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(bottom: 28),
child: Container(
width: double.infinity,
height: 150,
color: Color.fromRGBO(55, 55, 55, 0.2),
padding: EdgeInsets.all(15),
child: Text(
'Any container with bottom padding with half size of the FAB'),
),
),
Positioned(
bottom: 0,
right: 10,
child: FloatingActionButton(
child: Icon(
Icons.play_arrow,
size: 40,
),
onPressed: () => print('Button pressed!'),
),
),
],
),
);
}
}
CodePan link for anchor FAB
The correct solution is to use a "Stack" and "Positioned" widged like:
return Stack(
children: <Widget>[
Card(
color: Color(0xFF1D3241),
margin: EdgeInsets.only(bottom: 40), // margin bottom to allow place the button
child: Column(children: <Widget>[
...
],
),
Positioned(
bottom: 0,
right: 17,
width: 80,
height: 80,
child: FloatingActionButton(
backgroundColor: Color(0xFFF2638E),
child: Icon(Icons.play_arrow,size: 70,)
),
),
],
);

A problem in using ClipRRect() in Flutter

I have used ClipRRect for rounded corners in the UI. The ClipRRect wraps topContent and bottomContent are a stack and a Column respectively. But, the bottom corners are not round. What may be the reason behind this?
The cardModel class is used to store the image path in this case.
class FeaturedCard extends StatelessWidget {
final FeaturedCardModel cardModel;
final double parallaxPercent;
FeaturedCard({
this.cardModel,
this.parallaxPercent = 0.0, //default value
});
#override
Widget build(BuildContext context) {
final topContent = Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(
left: 10.0,
),
height: MediaQuery.of(context).size.height * 0.3,
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage(cardModel.imgUrl),
fit: BoxFit.fill,
),
)),
],
);
final bottomContentText = Text(
'This is the sample text',
style: TextStyle(fontSize: 18.0),
);
final bottomContent = Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width,
color: Colors.white,
padding: EdgeInsets.all(40.0),
child: Center(
child:
bottomContentText,
),
);
return ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Column(
children: <Widget>[
topContent,
bottomContent,
],
),
);
}
}
If you go to Flutter Inspector and do "Toggle Debug Paint" you will see that the clipping occurs in the blue area below.
You can fix it by giving a size to your clipper.
return SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Column(
children: <Widget>[
topContent,
//bottomContent,
],
),
),
);
The column that is the child of ClipRRect is taking as much space as it can get. so the border radius is applied to the bottom of the screen.
to solve that you just need to set mainAxisSize property of Column to MainAxisSize.min:
return ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
topContent,
bottomContent,
],
),
);

How to overlay a view partially using the Stack widget in Flutter

I am using Flutter for my app development.
I would like to overlay a poster image view on top of a background image just like in this screenshot below.
The code snippet below does this, but it requires me to also position every other widget including the movie title, release date, etc based on poster's position and background image's position, which is not reliable across several devices and orientation. Is there an example or suggestion to solve this problem?
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new PlatformAdaptiveAppBar(
title: new Text(widget.movie.title),
),
body: new Container(
constraints: new BoxConstraints.expand(),
child: new Stack(
children: <Widget>[
new Container(
child: new Image(
image: new AdvancedNetworkImage(
movieGridUtil.getUrlFromPath(widget.movie.backdrop_path,
MovieGridImageTypes.BACKDROP),
useMemoryCache: false,
useDiskCache: true)),
constraints: new BoxConstraints.expand(height: 250.0),
),
new Positioned(
left: 12.0,
top: 220.0,
child: new Image(
width: 100.0,
height: 150.0,
image: new AdvancedNetworkImage(
movieGridUtil.getUrlFromPath(widget.movie.poster_path,
MovieGridImageTypes.POSTER),
useMemoryCache: false,
useDiskCache: true),
)),
],
)),
);
}
Create Stack
Then inside Stack add Column and make full layout without the poster.
Then as a second Child of Stack, add this combination:
new Stack(
children: [
new Column(
children: _layout()
new Positioned(
top:200,
left:50,
child: _child // or optionaly wrap the child in FractionalTranslation
)]
)
)
Stack(
children: <Widget>[
Container(
color: Colors.blue,
height: 200.0,
),
Padding(
padding: const EdgeInsets.only(left: 20.0,right: 20.0, top:160.0),
child: Container(
color: Colors.pink,
height: 150.0,
width: 110.0,
),
)
],
),
By Creating the Stack,
You can add multiple Container, whichever is last added will be on the top.
Stack(
children: <Widget>[
Container(
color: Colors.blue,
height: 200.0,
),
Padding(
padding: const EdgeInsets.only(left: 20.0,right: 20.0, top:160.0),
child: Container(
color: Colors.pink,
height: 150.0,
width: 110.0,
),
)
],
),

Resources