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,)
),
),
],
);
Related
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#
I am currently learning flutter and have come across an issue where the card is not staying within the boundaries of Column, but is not causing an overflow. My goal is to have the card positioned at the bottom and with Text positioned above the card.
Here is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
child:
Image.asset("assets/testimage.jpg", fit: BoxFit.cover)),
Positioned(
bottom: 10.0,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
child: Text(
"A description would be going here, this is just placeholder text.",
style: TextStyle(fontSize: 30),
),
),
)
],
),
],
),
),
);
}
}
and an image example of the problem and desired outcome
Problem
Desired Outcome
You need to define - right: 1.0, left: 1.0, also along with bottom in Positioned. widget.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
child: Image.network("https://placeimg.com/640/480/any",
fit: BoxFit.cover)),
Positioned(
bottom: 1.0,
right: 1.0,
left: 1.0,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"A description would be going here, this is just placeholder text.",
style: TextStyle(fontSize: 30),
),
),
),
)
],
),
],
),
);
}
Output:
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,
],
),
);
Please how to create Side Radial Menu in flutter like a picture and make rolling when user tap in it
Any help would be appreciated.
This can be achieved by using a GestureDetector, Transform, trigonometry and some clipping with ClipRect.
Using GestureDetector, it is possible to see the drag distance that a user inputs. This can be used to determine how much to rotate the widgets.
Using Transform, it is possible to move widgets to specific locations.
Trigonometry is used to determine the position of the widgets to the centre of the circle.
Using ClipRect, it is possible to clip out the left side of the widgets.
It is possible to reverse scroll direction by instead taking the distance of the drag be turned negative.
Here is the code to make a rotating menu that uses a custom widget that I have recently created for answering this question(Add more Widgets to the Widget list if you want):
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body:CircularScrollView(//wrap this with align if you want it to be aligned to the right of the screen
[//add more widgets or remove as you'd like
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('a')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('b')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('c')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('d')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('e')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
],
radius: 100,
padding: 0,//add double the radius entered to clip out the right side
itemMaxHeight: 20,//effects clipping border height
itemMaxWidth: 20,//effects clipping border width
),
),
);
}
}
class CircularScrollView extends StatefulWidget {
final List<Widget> items;
final double radius;
final double itemMaxHeight;
final double itemMaxWidth;
final double padding;
final bool reverse;
CircularScrollView(this.items, {Key key, this.radius=10, this.itemMaxHeight=0, this.itemMaxWidth=0, this.padding=0, this.reverse=false}) : super(key: key);
#override
_CircularScrollViewState createState() => _CircularScrollViewState();
}
class _CircularScrollViewState extends State<CircularScrollView> {
double lastPosition;
List<Widget> transformItems= [];
double degreesRotated = 0;
#override
void initState() {
setState(() {
_calculateTransformItems();
});
super.initState();
}
void _calculateTransformItems(){
transformItems= [];
for(int i = 0; i<widget.items.length; i++){
double startAngle = (i/widget.items.length)*2*math.pi;
double currentAngle = degreesRotated+startAngle;
transformItems.add(
Transform(
transform: Matrix4.identity()..translate(
(widget.radius)*math.cos(currentAngle),
(widget.radius)*math.sin(currentAngle),
),
child: widget.items[i],
),
);
}
}
void _calculateScroll(DragUpdateDetails details){
if (lastPosition == null){
lastPosition = details.localPosition.dy;
return;
}
double distance = details.localPosition.dy - lastPosition;
double distanceWithReversal = widget.reverse?-distance:distance;
lastPosition =details.localPosition.dy;
degreesRotated += distanceWithReversal/(widget.radius);
_calculateTransformItems();
}
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: Container(
height: widget.radius*2+widget.itemMaxHeight,
width: widget.radius*2 + widget.itemMaxWidth,
child: GestureDetector(
onVerticalDragUpdate: (details)=>setState((){_calculateScroll(details);}),
onVerticalDragEnd: (details){lastPosition=null;},
child: Container(
height: double.infinity,
width: double.infinity,
color: Colors.transparent,
child: ClipRect(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(left: widget.padding),
child: Stack(
children: transformItems,
),
),
),
),
),
),
),
);
}
}
When using this code, do not modify the insides of the custom widget unless you know exactly what that section of the code does. When aligning the widget, please instead wrap the custom widget from the outside.
You can try using this package, circle_wheel_scroll, move around this widget inside Stack, place with Positioned with negative left position if necessary.
CircleListScrollView(
physics: CircleFixedExtentScrollPhysics(),
axis: Axis.horizontal,
itemExtent: 80,
children: List.generate(20, _buildItem),
radius: MediaQuery.of(context).size.width * 0.6,
),
or this listwheelscrollview
ListWheelScrollView(
itemExtent: 100,
// diameterRatio: 1.6,
// offAxisFraction: -0.4,
// squeeze: 0.8,
clipToSize: true,
children: <Widget>[
RaisedButton(onPressed:null ,
child: Text("Item 1",textAlign:TextAlign.start,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 2",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 3",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 4",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 5",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 6",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 7",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 8",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
],
),
Is there any way to show fullscreen image ?
var imagejadwal = new Image.network(
"https://firebasestorage.googleapis.com/v0/b/c-smp-bruder.appspot.com/o/fotojadwal.jpg?alt=media&token=b35b74df-eb40-4978-8039-2f1ff2565a57",
fit: BoxFit.cover
);
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: imagejadwal
),
);
in that code, there's space around the image :/
Your problem is that Center will make the image to get it's preferred size instead of the full size.
The correct approach would be instead to force the image to expand.
return new Scaffold(
body: new Image.network(
"https://cdn.pixabay.com/photo/2017/02/21/21/13/unicorn-2087450_1280.png",
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
alignment: Alignment.center,
),
);
The alignment: Alignment.center is unnecessary. But since you used the Center widget, I tought it would be interesting to know how to customize it.
Here is a View you wrap around your image widget
Includes a click event which opens up a full screen view of the image
Zoom and Pan image
Null-safety
Dark/Light background for PNGs
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ImageFullScreenWrapperWidget extends StatelessWidget {
final Image child;
final bool dark;
ImageFullScreenWrapperWidget({
required this.child,
this.dark = true,
});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
opaque: false,
barrierColor: dark ? Colors.black : Colors.white,
pageBuilder: (BuildContext context, _, __) {
return FullScreenPage(
child: child,
dark: dark,
);
},
),
);
},
child: child,
);
}
}
class FullScreenPage extends StatefulWidget {
FullScreenPage({
required this.child,
required this.dark,
});
final Image child;
final bool dark;
#override
_FullScreenPageState createState() => _FullScreenPageState();
}
class _FullScreenPageState extends State<FullScreenPage> {
#override
void initState() {
var brightness = widget.dark ? Brightness.light : Brightness.dark;
var color = widget.dark ? Colors.black12 : Colors.white70;
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: color,
statusBarColor: color,
statusBarBrightness: brightness,
statusBarIconBrightness: brightness,
systemNavigationBarDividerColor: color,
systemNavigationBarIconBrightness: brightness,
));
super.initState();
}
#override
void dispose() {
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
// Restore your settings here...
));
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: widget.dark ? Colors.black : Colors.white,
body: Stack(
children: [
Stack(
children: [
AnimatedPositioned(
duration: Duration(milliseconds: 333),
curve: Curves.fastOutSlowIn,
top: 0,
bottom: 0,
left: 0,
right: 0,
child: InteractiveViewer(
panEnabled: true,
minScale: 0.5,
maxScale: 4,
child: widget.child,
),
),
],
),
SafeArea(
child: Align(
alignment: Alignment.topLeft,
child: MaterialButton(
padding: const EdgeInsets.all(15),
elevation: 0,
child: Icon(
Icons.arrow_back,
color: widget.dark ? Colors.white : Colors.black,
size: 25,
),
color: widget.dark ? Colors.black12 : Colors.white70,
highlightElevation: 0,
minWidth: double.minPositive,
height: double.minPositive,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
onPressed: () => Navigator.of(context).pop(),
),
),
),
],
),
);
}
}
Example Code:
ImageFullScreenWrapperWidget(
child: Image.file(file),
dark: true,
)
This is another option:
return new DecoratedBox(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('images/lake.jpg'),
fit: BoxFit.fill
),
),
);
For Image from asset
new Image(
image: AssetImage('images/test.jpg'),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
alignment: Alignment.center,
),
For some reason, the solutions given in the answers here did not work for me. The below code worked for me.
body: Container(
height: double.infinity,
width: double.infinity,
child: FittedBox(child: Image.asset('assets/thunderbackground.jpg'),
fit: BoxFit.cover),
you could try wrapping image.network in a a container with infinite dimensions which takes the available size of its parent (meaning if you drop this container in lower half of screen it will fill the lower half of screen if you put this directly as the body of scaffold it will take the full screen)
Container(
height: double.infinity,
width: double.infinity,
child: Image.network(
backgroundImage1,
fit: BoxFit.cover,
)
);
You can use MediaQuery class if you want to get the precious size of your device and use it to manage the size of your image, here's the examples:
return Container(
color: Colors.white,
child: Image.asset(
'assets/$index.jpg',
fit: BoxFit.fill,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
),
);
Here is an example of a FadeInImage with another widget overlay using the double.infinity method as in the accepted answer.
class FullScreenImage extends StatelessWidget {
#override
Widget build(BuildContext context) {
//you do not need container here, STACK will do just fine if you'd like to
//simplify it more
return Container(
child: Stack(children: <Widget>[
//in the stack, the background is first. using fit:BoxFit.cover will cover
//the parent container. Use double.infinity for height and width
FadeInImage(
placeholder: AssetImage("assets/images/blackdot.png"),
image: AssetImage("assets/images/woods_lr_50.jpg"),
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
//if you use a larger image, you can set where in the image you like most
//width alignment.centerRight, bottomCenter, topRight, etc...
alignment: Alignment.center,
),
_HomepageWords(context),
]),
);
}
}
//example words and image to float over background
Widget _HomepageWords(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: Padding(
padding: EdgeInsets.all(30),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, 40, 0, 12),
child: Image.asset(
"assets/images/Logo.png",
height: 90,
semanticLabel: "Logo",
),
),
Text(
"ORGANIZATION",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Text(
"DEPARTMENT",
style: TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Text(
"Disclaimer information...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.white),
),
],
),
),
onTap: () {
//to another screen / page or action
},
),
],
);
}
Use the below code if height: double.infinity, width: double.infinity, doesn't work to u.
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => new _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Timer(Duration(seconds: 30),()=>Navigator.push(
context, MaterialPageRoute(builder: (context)=>Login())));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
//backgroundColor: Colors.white,
body: Container(
child: new Column(children: <Widget>[
new Image.asset(
'assets/image/splashScreen.png',
fit: BoxFit.fill,
// height: double.infinity,
// width: double.infinity,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
alignment: Alignment.center,
repeat: ImageRepeat.noRepeat,
),
]),
),
);
}
}