How to draw overlapped ractangles with some positioning in flutter? - flutter

I want to draw multiple rectangles like this
I have used CustomPainter to get desired result but getting something different.
I have shared the code and undesired output image below,
Please help me to draw the overlapped ractangles with some positioning to get desired output.
import 'package:flutter/material.dart';
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
// double _height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
width: _width,
// height: _height,
child: Center(
child: Stack(children: <Widget>[
CustomPaint(
painter: DrawCustomRect(0, 0, 80, 112, 0xFFFA2A2A),
),
CustomPaint(
painter: DrawCustomRect(0, 0, 16, 96, 0xFFFF5454),
),
CustomPaint(
// DrawCustomRect(_left,_top,_right, _bottom, _color);
painter: DrawCustomRect(0, 0, 16, 80, 0xFFFF8D8D),
),
]),
),
),
);
}
}
class DrawCustomRect extends CustomPainter {
final double _left;
final double _top;
final double _right; //width
final double _bottom; //height
final int _color;
DrawCustomRect(this._left, this._top, this._right, this._bottom, this._color);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTRB(this._left, this._top, this._right, _bottom),
new Paint()..color = new Color(_color),
);
}
#override
bool shouldRepaint(DrawCustomRect oldDelegate) {
return false;
}
}

You can achieve this result using Containers inside a Stack:
Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 300),
width: 75,
height: 100,
color: Colors.red[300],
),
Container(
margin: EdgeInsets.only(left: 150),
width: 150,
height: 200,
color: Colors.red[600],
),
Container(
width: 225,
height: 300,
color: Colors.red[900],
),
],
),
),
And then adjust the sizes and colors based on what you want.
The result:

There are two approaches to it. The first and easy one is to use a combination of stack and container widget to achieve the above effect. Here's the code I used
import 'package:flutter/material.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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
children: <Widget>[
Align(
alignment: Alignment(0.4, 0.1),
child: Container(
width: 60,
height: 60,
color: Colors.red[200],
),
),
Align(
alignment: Alignment(0.3, 0.1),
child: Container(
width: 70,
height: 70,
color: Colors.red[300],
),
),
Align(
alignment: Alignment(0.2, 0.1),
child: Container(
width: 80,
height: 80,
color: Colors.red[400],
),
),
Align(
alignment: Alignment(0.1, 0.1),
child: Container(
width: 90,
height: 90,
color: Colors.red[500],
),
),
],
),
),
);
}
}
The other one is by using the custom painter. You just have to fix some positioning in your code.
import 'package:flutter/material.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: SplashScreen(),
);
}
}
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
// double _height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
width: _width,
// height: _height,
child: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
CustomPaint(
painter: DrawCustomRect(112, 32, 0, 80, 0xFFFF8D8D),
),
CustomPaint(
painter: DrawCustomRect(96, 16, 0, 96, 0xFFFF5454),
),
CustomPaint(
painter: DrawCustomRect(80, 0, 0, 112, 0xFFFA2A2A),
),
],
),
),
),
);
}
}
class DrawCustomRect extends CustomPainter {
final double _left;
final double _top;
final double _right; //width
final double _bottom; //height
final int _color;
DrawCustomRect(this._left, this._top, this._right, this._bottom, this._color);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTRB(this._left, this._top, this._right, _bottom),
new Paint()..color = new Color(_color),
);
}
#override
bool shouldRepaint(DrawCustomRect oldDelegate) {
return false;
}
}

Related

Flutter: Increase hitbox of GestureDetector

I am fairly new to flutter and currently trying to create a NavBar.
It looks like this:
If I click on the icon, the bar moves to the selected one and the content changes.
However, I have to hit the icon perfectly. I would like to have a "box" around it, so I can tap just near it. Basically divide the space into 3.
I tried the following:
Widget build(BuildContext context) {
return Container(
height: 60,
color: Color(0xff282424),
child: Stack(
children: [
Container(
child: Row(
children: items.map((x) => createNavBarItem(x)).toList(),
),
),
AnimatedContainer(
duration: Duration(milliseconds: 200),
alignment: Alignment(active.offset, 0.7),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: 5,
width: 50,
decoration: BoxDecoration(
color: active.color,
borderRadius: BorderRadius.circular(2.5)),
),
),
],
),
);
}
Widget createNavBarItem(MenuItem item) {
double width = MediaQuery.of(context).size.width;
return SizedBox(
width: width / items.length,
height: 55,
child: GestureDetector(
child: Icon(
Icons.access_time,
color: item.color,
size: 30,
),
onTap: () {
setState(() {
active = item;
navBarUpdate(item);
});
},
),
);
}
The items should take 1/3 of the width. It isn't working that way tho. Any idea on how to increase the "tappable" space?
EDIT
Full code:
import 'package:flutter/material.dart';
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) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var screens = [Text("Button1"), Text("Button2"), Text("Button3")];
int currentScreen = 0;
void changeIndex(int index) => setState(() {
currentScreen = index;
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.orange,
child: Stack(
children: [
SafeArea(child: screens[currentScreen]),
Container(
alignment: Alignment.bottomCenter, child: NavBar(changeIndex))
],
),
),
);
}
}
class MenuItem {
final String name;
final Color color;
final double offset;
MenuItem(this.name, this.color, this.offset);
}
class NavBar extends StatefulWidget {
#override
State<StatefulWidget> createState() => NavBarState(navBarUpdate);
late Function(int) navBarUpdate;
NavBar(this.navBarUpdate);
}
class NavBarState extends State<NavBar> {
var items = [
MenuItem("Test", Colors.red, -0.76),
MenuItem("Test2", Colors.green, 0),
MenuItem("Test3", Colors.yellow, 0.76)
];
late MenuItem active;
late Function(MenuItem) navBarUpdate;
#override
void initState() {
super.initState();
active = items[0];
}
NavBarState(Function(int) navBarUpdate) {
this.navBarUpdate = (item) {
navBarUpdate(items.indexOf(item));
};
}
#override
Widget build(BuildContext context) {
return Container(
height: 60,
color: Color(0xff282424),
child: Stack(
children: [
Container(
child: Row(
children: items.map((x) => createNavBarItem(x)).toList(),
),
),
AnimatedContainer(
duration: Duration(milliseconds: 200),
alignment: Alignment(active.offset, 0.7),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: 5,
width: 50,
decoration: BoxDecoration(
color: active.color,
borderRadius: BorderRadius.circular(2.5)),
),
),
],
),
);
}
Widget createNavBarItem(MenuItem item) {
double width = MediaQuery.of(context).size.width;
return SizedBox(
width: width / items.length,
height: 55,
child: GestureDetector(
child: Icon(
Icons.access_time,
color: item.color,
size: 30,
),
onTap: () {
setState(() {
active = item;
navBarUpdate(item);
});
},
),
);
}
}
You can use behavior: HitTestBehavior.translucent, or opaque on createNavBarItem
child: GestureDetector(
behavior: HitTestBehavior.translucent,
You can swap your GestureDetector on top level widget from Icon.
Widget createNavBarItem(MenuItem item) {
double width = MediaQuery.of(context).size.width;
return GestureDetector(
child: Container(
color: Colors.transparent,
width: width / items.length,
height: 55,
child: Icon(
Icons.access_time,
color: item.color,
size: 30,
),
),
onTap: () {
setState(() {
active = item;
navBarUpdate(item);
});
},
);
}

Flutter - Container with ring progress indicator border

I am trying to achieve a container that will have a progress indicator border, like in this image:
I've tried to achieve it using a container inside another container (1 container for the outside white border, and one container for the inside blue background with the icon), but I can't achieve the progress indicator effect.
Does anyone know how can I achieve this?
Thank you
If you don't want to use a CustomPainter you can try to achieve that with a Stack widget
You can see this example in DartPad
Use the value property on the second CircularProgressIndicator to update the value with setState or any other State Management technique you like
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: const [
CircleAvatar(
backgroundColor: Colors.white,
radius: 24,
child: Icon(Icons.check),
),
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
color: Colors.grey,
value: 1,
),
),
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
color: Colors.blue,
value: .3, // Change this value to update the progress
),
),
],
);
}
}
There is a widget called CircularProgressIndicator that seems to be exactly what you're after.
How to use it:
CircularProgressIndicator(
backgroundColor: Colors.white,
color: Colors.purple.withAlpha(100),
strokeWidth: 5,
value: value, //
),
backgroundColor: for the white background
color: for the purple overlay
strokeWidth: for the thickness that you want
value: the actual progress of the indicator
And to have the arrow on top just use a round white Container (use a BoxDecoration with shape: BoxShape.circle to make it a circle), and put the arrow on top of it using the Stack widget.
Hope this helps!
class ProgressPainter extends CustomPainter {
final double value;
double deg2rad(double deg) => deg * pi / 180;
ProgressPainter({
required this.value,
});
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()..color = Colors.blueGrey;
final rect = Rect.fromCenter(
center: Offset(size.height / 2, size.width / 2),
width: size.width,
height: size.height);
canvas.drawArc(
rect,
deg2rad(-90),
deg2rad(
(value * 360) / 100, // % to degree
),
true,
paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
And use
CustomPaint(
painter: ProgressPainter(value: sliderVal),
child: const SizedBox(
height: 100,
width: 100,
child: Icon( // your inner widget
Icons.ac_unit,
size: 100,
),
),
),
Tested widget:
class _MyHomePageState extends State<MyHomePage> {
double sliderVal = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
children: [
CustomPaint(
painter: ProgressPainter(value: sliderVal),
child: const SizedBox(
height: 100,
width: 100,
child: Icon(
Icons.ac_unit,
size: 100,
),
),
),
Slider(
value: sliderVal,
min: 0,
max: 100,
onChanged: (value) {
setState(() {
sliderVal = value;
});
},
)
],
),
),
);
}
}

How to draw the line with sharp ends /gradient line/ in flutter?

Using stroke method, how to create gradient line with sharp end in flutter? I want to draw the line as below in flutter.
Use CustomPainter to draw:
import 'package:flutter/material.dart';
void main() => runApp(Example());
class Example extends StatefulWidget {
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: CustomPaint(
size: Size(200, 5),
painter: CurvePainter(),
),
)));
}
#override
void dispose() {
super.dispose();
}
}
class CurvePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.black;
paint.style = PaintingStyle.fill; // Change this to fill
var path = Path();
path.moveTo(0, 0);
path.quadraticBezierTo(size.width / 2, size.height / 2, size.width, 0);
path.quadraticBezierTo(size.width / 2, -size.height / 2, 0, 0);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
modified sleepingkit answer:
import 'package:flutter/material.dart';
class PointedLinePainter extends CustomPainter {
final double width;
PointedLinePainter(this.width);
#override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.white;
paint.style = PaintingStyle.fill;
var path = Path();
path.moveTo(0, 0);
path.quadraticBezierTo(width / 3, 0.5, width, 0);
path.quadraticBezierTo(width / 3, -0.5, 0, 0);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
somewhere in code:
Container(
margin: EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
width: MediaQuery.of(context).size.width,
height: 2,
child: CustomPaint(
painter:
PointedLinePainter(width - 2 * horizontalPointedLineMargin),
),
)
Can't see your picture.You can use the CustomPaint widget to draw lines, define a class that extends CustomPainter, and use the canvas.drawLine() method.
Divider class
A thin horizontal line, with padding on either side.
In the material design language, this represents a divider. Dividers can be used in lists, Drawers, and elsewhere to separate content.
To create a divider between ListTile items, consider using ListTile.divideTiles, which is optimized for this case.
The box's total height is controlled by height. The appropriate padding is automatically computed from the height.
[![// Flutter code sample for Divider
// This sample shows how to display a Divider between an orange and blue box
// inside a column. The Divider is 20 logical pixels in height and contains a
// vertically centered black line that is 5 logical pixels thick. The black
// line is indented by 20 logical pixels.
//
// !\[\](https://flutter.github.io/assets-for-api-docs/assets/material/divider.png)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: MyStatelessWidget(),
),
);
}
}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>\[
Expanded(
child: Container(
color: Colors.amber,
child: const Center(
child: Text('Above'),
),
),
),
const Divider(
color: Colors.black,
height: 20,
thickness: 5,
indent: 20,
endIndent: 0,
),
Expanded(
child: Container(
color: Colors.blue,
child: const Center(
child: Text('Below'),
),
),
),
\],
),
);
}
}]
Here info how to change style enter link description here
Row(
children: [
Expanded(
child: SizedBox(
height: 3,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.black],
begin: Alignment.centerRight,
end: Alignment.centerLeft
)
),
),
),
),
Expanded(
child: SizedBox(
height: 3,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.black],
begin: Alignment.centerLeft,
end: Alignment.centerRight
)
),
),
),
),
],
)

Diagonal design of a container

I want to make a container styled as follows:
https://i.stack.imgur.com/ZPS6H.png
Having no idea how to do that I've tried to just incorporate SVG but it takes a different amount of time to render rectangles than to display SVG.
I've tried LinearGradient but even when I define stops it doesn't look right.
Here's what I have now:
Container(
width: width,
height: 0.7 * height,
child: Row(
children: [
Container(
height: 0.7 * height,
width: width * 0.35,
color: yellow,
child: CircularPhoto(),
),
Container(
width: width * 0.15,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/divider#2x.png'),
fit: BoxFit.fill,
),
),
),
Container(
width: width * 0.50,
color: Colors.white,
child: BannerInfo(),
),
],
),
);
This is an example!:
Maybe copy and paste it here to try it!: https://dartpad.github.io/
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height:200,
width:500,
child: Stack(
children:[
Container(
color:Colors.white
),
ClipPath(
child: Container(
width: MediaQuery.of(context).size.width,
color: Colors.yellow,
),
clipper: CustomClipPath(),
)
]
)
)
;
}
}
class CustomClipPath extends CustomClipper<Path> {
var radius=10.0;
#override
Path getClip(Size size) {
Path path = Path();
path.lineTo(0, 200);
path.lineTo(200,200);
path.lineTo(260,0);
path.lineTo(30, 0);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

flutter - create an elastic effect on animation

I have a container in a Stack widget of height 100.0. It is positioned in the center using a Positioned widget as follows
Container(
width:100.0,
height:100.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: 40.0,
child: Container(
width: 20.0,
height: 20.0,
color: Colors.red,
),
)
],
)
)
I want to animate the red container in such a way that when clicked it goes to bottom of the parent container and when clicked angain bounces back to top then back to center.
I tried using Curves.elasticOut but that is not enough bounce for me.
How do I acheive this effect
Try this code and let me know if I understand your animation correctly
Edited:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'dart:math';
const BOX_COLOR = Colors.cyan;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Padding(
child: PhysicsBox(),
padding: EdgeInsets.only(
left: 20.0,
right: 20.0,
top: 20.0,
bottom: 20.0,
),
),
));
}
}
class PhysicsBox extends StatefulWidget {
#override
BoxState createState() => BoxState();
}
class BoxState extends State<PhysicsBox> with TickerProviderStateMixin {
AnimationController controller;
AnimationController controller2;
Animation<double> animation;
SpringSimulation simulation;
double _position;
#override
void initState() {
super.initState();
simulation = SpringSimulation(
SpringDescription(
mass: 1.0,
stiffness: 100.0,
damping: 5.0,
),
400.0,
208.0,
-4000.0,
);
controller2 = AnimationController(vsync: this,duration: Duration(milliseconds: 70));
animation = Tween(begin: 200.0, end: 400.0).animate(controller2)
..addListener((){
if(controller2.status == AnimationStatus.completed){controller.reset();}
setState(() {
_position = animation.value;
});
});
controller = AnimationController(vsync: this,duration: Duration(milliseconds: 700))..forward()
..addListener(() {
if(controller.status == AnimationStatus.completed){controller2.reset();}
setState(() {
_position = simulation.x(controller.value);
});
print('${simulation.x(controller.value)}');
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
width:500.0,
height:500.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: _position,
child: GestureDetector(
onTap: (){
if (controller.status == AnimationStatus.completed) {
controller2.forward();//controller.reset();
}else{
controller.forward();}
},
child: Container(
width: 100.0,
height: 100.0,
color: Colors.red,
),
),
)
],
)
);
}
}
Use TweenMax for Flutter: https://pub.dartlang.org/packages/tweenmax
Wrap your container by a GestureDetector to use Tap Gesture, then replace your red container with TweenContainer:
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'package:tweenmax/tweenmax.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Spring Box",
theme: ThemeData(
primaryColor: Colors.red,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
bool isClicked = false;
#override
Widget build(BuildContext context) {
TweenContainer redContainer = TweenContainer(
data: TweenData(
top: 40,
width: 20.0,
height: 20.0,
color: Colors.red,
),
);
return Scaffold(
body: GestureDetector(
child: Container(
width: 100.0,
height: 100.0,
margin: EdgeInsets.only(top: 100, left: 100),
color: Colors.yellow,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
redContainer
],
)
),
onTap: (){
// click first time, animate "redContainer" to bottom:
if(!isClicked){
TweenMax.to(
redContainer,
duration: 0.3,
ease: Curves.ease,
data: TweenData(
top: 80
)
);
} else { // click second time, animate "redContainer" to top:
TweenMax.to(
redContainer,
duration: 0.2,
ease: Curves.easeIn,
data: TweenData(
top: 0
),
onComplete: (redContainer){
// animate it back to center position:
TweenMax.to(
redContainer,
duration: 0.8,
ease: ElasticOutCurve(0.3),
data: TweenData(
top: 40
)
);
}
);
}
isClicked = !isClicked;
},
)
);
}
}
See this video: https://drive.google.com/file/d/1i3BgxWiVna6kQMRKVgUEQDTW2QTlSXWo/view?usp=sharing