Related
I am trying to make an audio player app,
and I want to make the player screen fit the whole screen size.
However, the padding at the top and at the bottom doesn't help.
I tried to remove the SafeArea from bottomNavigationBar and other widgets and it didn't work.
How can I handle this?
Image of the player:
(the gray color padding doesn't let the image stretch to the end)
the code of the player:
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff1c1c1e),
body: GetBuilder<OverlayHandler>(
builder: (getContext) {
if (!Get.find<OverlayHandler>().inPipMode) {
return Stack(
children:[
Container(...)
]
); // player at full screen
} else {
return Stack(...); // player at PiP mode
}
}
)
);
}
the code of the main screen widget:
Widget build(BuildContext context) {
return GetBuilder<NavigationController>(
builder: (controller) {
return Scaffold(
body: SafeArea(
// bottom option of this SafeArea doesn't affect the player size
child: IndexedStack(
index: controller.tabIndex,
children: const [
...
],
),
),
bottomNavigationBar: SafeArea(
// bottom option of this SafeArea doesn't affect the player size
child: SizedBox(
height: 80,
child: BottomNavigationBar(
items: [
...
],
),
),
),
);
}
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
TextEditingController controller = TextEditingController();
bool hasHash = false;
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Container(
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"https://cdn.pixabay.com/photo/2016/09/10/11/11/musician-1658887_1280.jpg",
),
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 300,
width: double.infinity,
color: Colors.black.withOpacity(.7),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(
Icons.skip_previous_rounded,
size: 55,
color: Colors.white,
),
Icon(
Icons.play_circle_fill_rounded,
size: 110,
color: Colors.white,
),
Icon(
Icons.skip_next_rounded,
size: 55,
color: Colors.white,
),
],
),
),
),
],
),
);
}
}
Android screenshot
iOS screenshot
Try removing the Scaffold()'s background color and add extendBody: true, or set the height of the container to height: double.infinity, or inside the stack just add and empty container with height as height: double.infinity,
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;
});
},
)
],
),
),
);
}
}
so here's the deal. I created (sort of) a custom clipper shaped like a wave inside a class called WaveClipper
the wave clipper class:
class WaveClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0, 220);
path.quadraticBezierTo(size.width / 4, 160 , size.width / 2, 175);
path.quadraticBezierTo(3 / 4 * size.width, 190, size.width, 130);
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
And whenever I display it using a scaffold it shows fine however when I try to push it inside a SliverListView which is inside a CustomScrollView then nothing appears and there are no errors either. Is the clipper under the content? And how can I display it.
the clipper I am trying to show:
Stack(
children: [
ClipPath(
clipper: WaveClipper(),
child: Container(
color: Colors.cyanAccent,
))
],
),
where I am trying to show it:
Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0.0,
iconTheme: IconThemeData(
color: Colors.cyanAccent,
),
),
backgroundColor: Colors.white,
body: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
SliverList(
delegate: SliverChildListDelegate([
//here
//there rest of the content (mostly buttons)
]),
),
],
),
)
Any help is appreciated and thank you for taking the time.
Try to give some dimension to your Container:
Stack(
children: [
ClipPath(
clipper: WaveClipper(),
child: Container(
height: 300,
color: Colors.amber.shade200,
),
),
],
),
Full source code
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0.0,
iconTheme: IconThemeData(
color: Colors.cyanAccent,
),
),
backgroundColor: Colors.white,
body: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
SliverList(
delegate: SliverChildListDelegate([
Stack(
children: [
ClipPath(
clipper: WaveClipper(),
child: Container(
height: 300,
color: Colors.amber.shade200,
),
),
],
),
]),
),
],
),
);
}
}
class WaveClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0, 220);
path.quadraticBezierTo(size.width / 4, 160, size.width / 2, 175);
path.quadraticBezierTo(3 / 4 * size.width, 190, size.width, 130);
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
Container(
height: device.size.height * 0.3,
child: Scaffold(
body: Stack(
clipBehavior: Clip.none,
children: [
ClipPath(
clipper: WaveClipper(),
child: Container(
color: Colors.cyanAccent,
))
],
),
),
),
Wrap with a Scaffold and give that Scaffold a size using a Container, if anyone has a better solution by all means post it.
import 'package:flutter/material.dart';
class LoginScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Stack(
children: [
Opacity(opacity: 0.5,
child: ClipPath(
clipper: WaveClipper(),
child: Container(color: Colors.red,
height: 200,),
),
),
ClipPath(
clipper: WaveClipper(),
child: Container(color: Colors.deepOrange,
height: 180,),
),
],
),
),
);
}
}
class WaveClipper extends CustomClipper<Path>{
#override
Path getClip(Size size){
debugPrint(size.width.toString());
var path = new Path();
path.lineTo(0,size.height);
var firstStart = Offset(size.width / 5,size.height);
var firstEnd = Offset(size.width / 2.25,size.height - 50);
path.quadraticBezierTo(firstStart.dx, firstStart.dy, firstEnd.dx, firstEnd.dy);
var secondStart = Offset(size.width -(size.width/3.24), size.height - 105);
var secondEnd = Offset(size.width, size.height - 10);
path.quadraticBezierTo(secondStart.dx, secondStart.dy, secondEnd.dx, secondEnd.dy);
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
Optionally add dotted_decoration package if needed:
https://pub.dev/packages/dotted_decoration
import 'package:flutter/material.dart';
import 'package:dotted_decoration/dotted_decoration.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: DiscountTile()),
);
}
}
class DiscountTile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ClipPath(
clipper: _DiscountTileClipper(),
child: Container(
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
),
child: ColoredBox(
color: Colors.blue,
child: Row(
children: [
SizedBox(width: 100),
Expanded(
child: DecoratedBox(
decoration: DottedDecoration(
linePosition: LinePosition.left,
color: Colors.red,
strokeWidth: 3,
),
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text('hello world' * 20),
),
),
),
],
),
),
),
);
}
}
class _DiscountTileClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
const Radius radius = Radius.circular(1);
const bool clockwise = false;
const double clipRadius = 20;
const double decreaseValueWidgetWidth = 100.0;
const double leftPadding = decreaseValueWidgetWidth + 9;
return Path()
..lineTo(leftPadding - clipRadius, 0)
..arcToPoint(
const Offset(leftPadding, 0),
clockwise: clockwise,
radius: radius,
)
..lineTo(size.width, 0)
..lineTo(size.width, size.height)
..lineTo(leftPadding, size.height)
..arcToPoint(
Offset(leftPadding - clipRadius, size.height),
clockwise: clockwise,
radius: radius,
)
..lineTo(0, size.height)
..close();
}
#override
bool shouldReclip(_DiscountTileClipper oldClipper) => true;
}
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;
}
}
I'd like the CircleAvatar to take a specific percentage (e.g. 50%) of the available space instead of specifying a fixed radius. How would you approach that?
I am not sure this is the best solution.
You can use LayoutBuilder to get the max constraints then build according to it.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
color: Colors.red,
height: 100.0,
width: 100.0,
child: _buildCircleAvatar(),
),
Container(
color: Colors.green,
height: 300.0,
width: 300.0,
child: _buildCircleAvatar(),
),
],
),
);
}
Widget _buildCircleAvatar() {
return LayoutBuilder(
builder: (context, constraints) {
final radius = min(constraints.maxHeight / 4, constraints.maxWidth / 4);
return Center(
child: CircleAvatar(
radius: radius,
backgroundImage: NetworkImage(
"https://as.ftcdn.net/r/v1/pics/7b11b8176a3611dbfb25406156a6ef50cd3a5009/home/discover_collections/optimized/image-2019-10-11-11-36-27-681.jpg",
),
),
);
},
);
}
}
Container(
color: Colors.green,
height: MediaQuery.of(context).size.height/2,
width: MediaQuery.of(context).size.width/2 ,
child:CircleAvatar(),
)
you can use this to get the responsive avatar