Flutter - Container with ring progress indicator border - flutter

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;
});
},
)
],
),
),
);
}
}

Related

How to go about creating this clippath design with a background?

How would I go about making this design in a ScrollView?
I was thinking about making two containers one with the yellow color & second with the white color and then using clipPath to morph the edges of the white container. But, the problem I would face is that the background image would leave space below the left edge of yellow container which would seem odd so, I would have to absolute position the white container on the Y axis and this entirely would be in a ScrollView which seemed kind of hard to achieve. So, what would be the best suited method to accomplish this?
Thanks.
One way is to use Stack to stack the background image and the column which contains the top textfield and the bottom white container. Then you clip the bottom container
Code example:
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Material App Bar'),
),
body: Builder(
builder: (context) {
return Stack(
alignment: Alignment.topCenter,
children: [
Container(
color: Color.fromRGBO(4, 20, 31, 1),
),
Padding(
padding: const EdgeInsets.only(top: 50.0),
child: Column(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
child: TextField(
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
)
),
),
),
Expanded(
child: ClipPath(
clipper: MyClipper(radius: 50),
child: Container(
color: Colors.white,
width: MediaQuery.of(context).size.width,
),
),
)
],
),
)
],
);
}
),
),
);
}
}
class MyClipper extends CustomClipper<Path>{
final double radius;
MyClipper({required this.radius});
#override
Path getClip(Size size) {
return Path()
..lineTo(0, 2*radius)
..arcTo(
Rect.fromCircle(center: Offset(radius, 2*radius), radius: radius),
math.pi,
math.pi/2,
false
)
..lineTo(radius, radius)
..lineTo(size.width-radius, radius)
..arcTo(
Rect.fromCircle(center: Offset(size.width-radius, 0), radius: radius),
math.pi/2,
-math.pi/2,
false
)
..lineTo(size.width, size.height)
..lineTo(0, size.height)
..close()
;
}
#override
bool shouldReclip(MyClipper oldClipper) {
return false;
}
}

Flutter - How to add a widget inside the track of a Slider widget

I have a simple Slider widget and I want to add multiple containers inside the track at different points.
Needed Outcome Image
Current Code
Slider(
max: 500,
value: _playerValue,
onChanged: (double value){
setState(() {
_playerValue = value;
});
},
),
I do not think this is possible only by using the SliderThemeData; or at least, no way that I am aware of. However, you can achieve this behaviour using Stack, with the markers shown behind a translucent track of the slider. Demo snippet below.
One thing to note is to make sure the container that has the stack of markers has horizontal margin that is same as the slider's overlay radius; otherwise, the marker container becomes wider than the track.
IMPORTANT: I put this snippet together rapidly only to demonstrate the possibility of showing markers behind a slider's track. The dimensions of the track and the markers are hard-coded, which is very likely not what you want. I do not recommend using it as is, but only as a guide.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Slider with Markers',
theme: ThemeData(
primarySwatch: Colors.blue,
brightness: Brightness.dark,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
double sliderValue = 50;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Slider With Markers"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white60,
inactiveTrackColor: Colors.white30,
thumbColor: Colors.white,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10),
overlayShape: RoundSliderOverlayShape(overlayRadius: 12),
trackHeight: 12,
),
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: double.infinity,
margin: const EdgeInsets.symmetric(horizontal: 12),
child: Stack(
alignment: Alignment.centerLeft,
children: <Widget>[
marker(left: 15, width: 30),
marker(left: 150, width: 50),
marker(left: 300, width: 100),
],
),
),
Slider(
value: sliderValue,
min: 0,
max: 100,
onChanged: ((value) {
setState(() {
sliderValue = value;
});
}),
),
],
),
)
],
),
),
);
}
Widget marker({required double left, required double width}) {
return Container(
height: 12,
width: width,
margin: EdgeInsets.only(left: left),
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(18),
),
);
}
}

How to achieve an expandable bottom navigation bar in Flutter

I am trying to build an app that includes a bottom navigation bar for navigating between screens.
In the middle of the bottom navigation bar, I want to add a button which expands the bottom navigation bar with a semicircle, and revels more buttons.
I've read the documentation of the bottom navigation bar, and searched a lot in pub.dev if there is something similar I can use, but I couldn't find any.
Does anyone know if it's achievable, and if so, how?
Thank you very much
You can check this simple implementation with showDialog and CustomPainter. Basically it involved displaying a showDialog with bottom padding equals the height of BottomNavigationBar, then arrange the items within a Stack. The half circle is drawn using CustomPainter.
Full example app:
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test App'),
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.blue,
showUnselectedLabels: true,
selectedFontSize: 14,
unselectedFontSize: 14,
type: BottomNavigationBarType.fixed,
onTap: (index) {
if (index == 2) {
final diameter = 200.0;
final iconSize = 40;
showDialog(
context: context,
barrierDismissible: true,
barrierColor: Colors.grey.withOpacity(0.1),
builder: (context) => Material(
color: Colors.transparent,
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
Container(
width: diameter + iconSize,
height: diameter / 1.5,
alignment: Alignment.bottomCenter,
margin:
EdgeInsets.only(bottom: kBottomNavigationBarHeight),
child: Stack(
children: [
Container(
alignment: Alignment.bottomCenter,
child: MyArc(diameter: diameter)),
Positioned(
left: 0,
bottom: 10,
child: _buildButton(),
),
Positioned(
left: diameter / 4,
top: 10,
child: _buildButton(),
),
Positioned(
right: diameter / 4,
top: 10,
child: _buildButton(),
),
Positioned(
right: 0,
bottom: 10,
child: _buildButton(),
)
],
),
),
],
),
),
);
}
},
items: List<BottomNavigationBarItem>.generate(
5,
(index) =>
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
),
),
);
}
_buildButton() {
return Container(
constraints: BoxConstraints.tightFor(width: 40, height: 60),
child: Column(
children: [
Text(
'Title',
style: TextStyle(fontSize: 12),
),
SizedBox(height: 3),
CircleAvatar(
backgroundColor: Colors.white,
child: Icon(Icons.home),
),
],
),
);
}
}
class MyArc extends StatelessWidget {
final double diameter;
const MyArc({Key key, this.diameter = 200}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: MyPainter(),
size: Size(diameter, diameter),
);
}
}
// This is the Painter class
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..shader = RadialGradient(
colors: [
Colors.blue,
Colors.purpleAccent.withOpacity(0.4),
],
).createShader(Rect.fromCircle(
center: Offset(size.width / 2, size.height),
radius: 200,
));
canvas.drawArc(
Rect.fromCenter(
center: Offset(size.width / 2, size.height),
height: size.height * 1.5,
width: size.width,
),
math.pi,
math.pi,
false,
paint,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Result:
I think you need to learn about Flutter Animated Radial Menu and how to implement it in your code, you can go with this article and try to implement in your way.

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;
}

How to draw overlapped ractangles with some positioning in 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;
}
}