Flutter draw container with a curve in the center - flutter

I'd like to draw the black container shape with flutter.

There are many ways to do this, the first time I thought it was a cut, I was thinking in a ClipPath widget, but now I see you have a Circle widget at the top of your black Container.
This is an example:
class FunnyContainer extends StatelessWidget {
const FunnyContainer({Key? key}) : super(key: key);
Widget _childContainer() {
return Padding(
padding: const EdgeInsets.all(20.0),
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.yellow,
width: 4,
),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 40),
child: Container(
height: 400,
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 40),
Expanded(child: _childContainer()),
Expanded(child: _childContainer()),
Expanded(child: _childContainer()),
],
),
const Positioned(
left: 0,
right: 0,
top: -50,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: 40,
),
)
],
),
),
),
);
}
}
Result:
UPDATE (Using your updated design)
Now the you updated your post, this could be done using Clippers, the problem is that clip needs Shadow, so I took this code : https://gist.github.com/coman3/e631fd55cd9cdf9bd4efe8ecfdbb85a7
And I used on this example:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: ClipShadowPath(
clipper: _MyClipper(100),
shadow: const Shadow(
blurRadius: 15,
color: Colors.grey,
offset: Offset(0, 10),
),
child: SizedBox(
height: 400,
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
Spacer(),
],
),
),
),
),
),
),
);
}
}
class _MyClipper extends CustomClipper<Path> {
final double space;
_MyClipper(this.space);
#override
Path getClip(Size size) {
final path = Path();
final halfWidth = size.width / 2;
final halfSpace = space / 2;
final curve = space / 6;
final height = halfSpace / 1.4;
path.lineTo(halfWidth - halfSpace, 0);
path.cubicTo(halfWidth - halfSpace, 0, halfWidth - halfSpace + curve,
height, halfWidth, height);
path.cubicTo(halfWidth, height, halfWidth + halfSpace - curve, height,
halfWidth + halfSpace, 0);
path.lineTo(size.width, 0);
path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
path.close();
return path;
}
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
RESULT
You just need to update the Path in order to get the rounded corners of the Container.

You may use Stack which may write draw circle on top of the container like this:
class BlackContainer extends StatelessWidget {
const BlackContainer({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Container(
child: child,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.black,
boxShadow: const [
BoxShadow(
color: Colors.grey,
spreadRadius: 5,
blurRadius: 10,
),
],
),
),
Positioned(
top: -25,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
width: 40,
height: 40,
),
),
],
);
}
}
And use it like that:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: BlackContainer(
child: Text(
'Content here',
style: Theme.of(context)
.textTheme
.bodyText2!
.copyWith(color: Colors.white),
),
),
),
);
}
}

Position a transparent png on top of your container.
child: Container(
width: 200,
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.black,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3),
),
],
),
child: Container(
child: Image.asset('assets/images/half-circle.png', fit: BoxFit.contain, alignment: new Alignment(-1.0, -1.0)),
margin: EdgeInsets.only(left: 50.0, right: 50.0),
),
),
Image:
Live example

Related

Need help to build custom widget in flutter

Hey I try to create a custom widget like this, where I can change the text in the bottom part of the card.
I already create the shape but I have no idea on how to place Text('Aktif'), I tried using Stack or Column but none works, any Idea?
Here is my code:
#override
Widget build(BuildContext context) {
return Stack(
children: [
Card(
shape: CardShape(),
elevation: 18,
shadowColor: const Color.fromRGBO(51, 0, 0, 0.1),
color: CustomStyle.primaryRed,
child: Container(
decoration: BoxDecoration(
color: CustomStyle.white,
border: Border.all(color: CustomStyle.primaryRed),
borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: const Text('Rumah Tanggerang'),
subtitle: const Text('Primary - 1234567890'),
trailing: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'DEFAULT',
),
myPopMenu()
],
),
),
),
),
Positioned(left: 10, bottom: 0, child: Text('Aktif'))
],
);
}
class CardShape extends ShapeBorder {
const CardShape();
final BorderSide _side = BorderSide.none;
final BorderRadiusGeometry _borderRadius = BorderRadius.zero;
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.all(_side.width);
#override
Path getInnerPath(
Rect rect, {
TextDirection? textDirection,
}) {
final Path path = Path();
path.addRRect(
_borderRadius.resolve(textDirection).toRRect(rect).deflate(_side.width),
);
return path;
}
#override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
final Path path = Path();
final RRect rrect = _borderRadius.resolve(textDirection).toRRect(rect);
path.moveTo(0, 10);
path.quadraticBezierTo(0, 0, 10, 0);
path.lineTo(rrect.width - 10, 0);
path.quadraticBezierTo(rrect.width, 0, rrect.width, 10);
path.lineTo(rrect.width, rrect.height - 10);
path.quadraticBezierTo(
rrect.width, rrect.height, rrect.width - 10, rrect.height);
path.lineTo(100, rrect.height);
path.lineTo(80, rrect.height + 20);
path.lineTo(10, rrect.height + 20);
path.quadraticBezierTo(0, rrect.height + 20, 0, rrect.height + 10);
return path;
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
#override
ShapeBorder scale(double t) => RoundedRectangleBorder(
side: _side.scale(t),
borderRadius: _borderRadius * t,
);
}
Card widget can't used stack because the red container is already outside the Card element so to put the text you just can wrap Stack inside of a Column and put the Text widget without position this is the example that I've already did
class Test extends StatelessWidget {
const Test({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
decoration: const BoxDecoration(color: Colors.blue),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
Card(
shape: const CardShape(),
elevation: 18,
shadowColor: const Color.fromRGBO(51, 0, 0, 0.1),
color: Colors.red,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.red),
borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: const Text('Rumah Tanggerang'),
subtitle: const Text('Primary - 1234567890'),
trailing: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: const <Widget>[
Text(
'DEFAULT',
),
],
),
),
),
),
],
),
const Padding(
padding: EdgeInsets.only(
left: 30,
),
child: Text(
'Aktif',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w700),
),
)
],
),
),
),
);
}
}
if you copy my code just made sure to change back the color you've used.

How to create UI like this in Flutter

I'm building an App I have to build UI like below but I don't have any idea that how to create UI like this. Kindly guide me through this.
var listImages = [
"https://source.unsplash.com/random/200x200?sig=1",
"https://source.unsplash.com/random/200x200?sig=2",
"https://source.unsplash.com/random/200x200?sig=3",
"https://source.unsplash.com/random/200x200?sig=4",
"https://source.unsplash.com/random/200x200?sig=5"
];
Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: [
for (int i = 0; i < (listImages.length>=4?4:listImages.length); i++)
Transform.translate(
offset: Offset(i == 0 ? 0.0 : i * 44, 0),
child: Container(
height: 70,
width: 70,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.black87,
),
clipBehavior: Clip.hardEdge,
child: (i+1)>=4?Text("+ ${listImages.length-3}",style:const TextStyle(color: Colors.green),):Image.network(listImages[i]),
),
)
],
),
)
Below are the methods used to prepare the layout
// this method return the layout as per your expectations, here images are the // list of items you want to use and max count are the list of max item you want // to show except the count tile.
Widget getItems(List<String> images, int maxCount) =>
Stack(
children: List.generate(images.length <= maxCount ? images.length : maxCount+1, (index) {
if(index == maxCount){
return Positioned(
left: index * 60,
child: Container(
padding: const EdgeInsets.all(2), // Border width
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 2,
),
color: Colors.black,
borderRadius: BorderRadius.circular(40)),
child: SizedBox.fromSize(
size: const Size.fromRadius(48), // Image radius
child: Center(
child: Text(
style: const TextStyle(
color: Color(0xff58D56D),
fontSize: 30
),
"+${images.length-maxCount}"
),
),
),
),
);
}
else {
return Positioned(
left: index * 60, child: getItemWidget(images[index]));
}
}),
);
// pass the image url you want to show.
Widget getItemWidget(String imageUrl) => Stack(
children: [
Container(
padding: const EdgeInsets.all(2), // Border width
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(40)),
child: ClipRRect(
borderRadius: BorderRadius.circular(40),
child: SizedBox.fromSize(
size: Size.fromRadius(48), // Image radius
child: Image.network(
imageUrl,
fit: BoxFit.fill,
),
),
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
padding: const EdgeInsets.all(2), // Border width
decoration: const BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: SizedBox.fromSize(
size: Size.fromRadius(20), // Image radius
child: Image.network(
"https://play-lh.googleusercontent.com/STIZ_iftiehDCSynHXQaLqiL-F4kbZwasXOB2nae5pXTOpNKz8XSd7_VCF1Zgc3Z8Q",
fit: BoxFit.contain,
),
),
),
),
)
],
);
Below code is used to show the items
getItems(["https://cdn.pixabay.com/photo/2013/07/13/10/07/man-156584__340.png",
"https://static.vecteezy.com/system/resources/thumbnails/001/993/889/small/beautiful-latin-woman-avatar-character-icon-free-vector.jpg",
"https://static.toiimg.com/thumb/resizemode-4,msid-76729536,width-1200,height-900/76729536.jpg",
"https://www.nj.com/resizer/zovGSasCaR41h_yUGYHXbVTQW2A=/1280x0/smart/cloudfront-us-east-1.images.arcpublishing.com/advancelocal/SJGKVE5UNVESVCW7BBOHKQCZVE.jpg",
"https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png"], 3),
)
Output:
The point is using Stack and Positioned to positiond widgets.
(index to padding, maxPerson to set maximum widget to show)
https://dartpad.dev/ example
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final colors = const [
Colors.yellow,
Colors.green,
Colors.blue,
Colors.orange,
Colors.cyan,
Colors.brown,
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: PersonStacker(colors: colors),
),
);
}
}
class PersonStacker extends StatelessWidget {
final List<Color> colors;
final int maxPerson;
const PersonStacker({required this.colors, this.maxPerson = 3});
#override
Widget build(BuildContext context) {
return Stack(
children: [
...colors
.getRange(0, maxPerson)
.toList()
.asMap()
.map((index, color) => MapEntry(index, Person(index, color: color)))
.values
.toList(),
...colors.length > maxPerson
? [Person(maxPerson, plus: colors.length - maxPerson)]
: []
],
);
}
}
class Person extends StatelessWidget {
final int index;
final Color color;
final Color colorBorder;
final double size = 100;
final double offset = 30;
final int? plus;
const Person(this.index,
{this.plus, this.color = Colors.black, this.colorBorder = Colors.black});
Widget renderChild() => plus != null
? Center(
child: Text(
"+$plus",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
)
: Container();
#override
Widget build(BuildContext context) {
return Positioned(
left: index * offset,
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: color,
border: Border.all(width: 2, color: colorBorder),
borderRadius: BorderRadius.circular(size),
),
child: renderChild(),
),
);
}
}
Result

flutter - PageView Builder & Card - displaying different info on each card

I am trying to use PageView and Card. My objective is to display different information on the fourth cards that the user can see.
To be more precise, on one card, I could display a title for example. On the second card, I will display dates, on the third card, it could be some random text...
I hope this is clear.
To give you more color on this, the information will come from FireBase.
I do not know how to do this easily. I have considered to create 4 widget myCard, with different info on it and to use them. But I would like to do something more professional and efficient. Thank you
About FireBase, I do not use json. I have a simple document in FireBase. In this document, I have several fields, like 'price' 'quantity' 'reference'...
I just want that each card to display two or three fields from this FireBase document.
class MyBodyWindMill extends StatefulWidget {
const MyBodyWindMill({Key key}) : super(key: key);
#override
_MyBodyWindMillState createState() => _MyBodyWindMillState();
}
class _MyBodyWindMillState extends State<MyBodyWindMill> {
#override
Widget build(BuildContext context) {
return
Container(
height: 260,
child: PageView.builder(controller: PageController(viewportFraction: 0.88),
itemCount: 4,
itemBuilder: ( _, i){
return GestureDetector(
child: Container(
padding: const EdgeInsets.only(left:20, top:20),
height: 220,
width: MediaQuery.of(context).size.width-20,
margin: const EdgeInsets.only(right:10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
),
child:Column(
children: [
MyCard(),
],
))
);
},
));
}}
class MyCard extends StatefulWidget {
const MyCard( {Key key} ) : super(key: key);
#override
_MyCardState createState() => _MyCardState();
}
class _MyCardState extends State<MyCard> {
//String monText;
#override
Widget build(BuildContext context) {
return Container(
height: 230,
child: Stack(
children: [
Positioned(
top: 35,
left: 20,
child: Card(
elevation: 6.0,
shadowColor: Colors.grey,
child: Container(
height: 180,
width: 0.9,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(0.0),
boxShadow: [BoxShadow(
color: Colors.black.withOpacity(0.3),
offset: Offset(-10.0, 10.0),
blurRadius: 20.0,
spreadRadius: 4.0,
),
])
),
),
),
Positioned(
top: 0.0,
left: 30.0,
child:Card(
elevation: 10.0,
shadowColor: Colors.grey.withOpacity(0.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Container(
height: 200,
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
image: DecorationImage(
fit: BoxFit.fill,
image:AssetImage('assets/header.jpg'),
),
),
))),
Positioned(
top:40,
left:200,
child:
Container(
height: 150,
width: 170,
child:Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Text('Title: ', style: TextStyle(fontSize:16,
color:Colors.red),),
Divider(color:Colors.black),
Text('Info 1: ', style: TextStyle(fontSize:16,
color:Colors.red),),
Text('Info 2'),
])
))
],
),
);
}
}
Based on what you're trying to do it looks like what you need is something like this widget:
class InfoBlock {
final String title;
final String description;
InfoBlock({
required this.title,
required this.description,
});
}
class MyCard extends StatefulWidget {
String title;
List<InfoBlock> infoBlocks;
MyCard({
Key? key,
required this.title,
required this.infoBlocks,
}) : super(key: key);
#override
_MyCardState createState() => _MyCardState();
}
class _MyCardState extends State<MyCard> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
image: const DecorationImage(
fit: BoxFit.fill,
image: AssetImage('header.jpeg'),
))),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: const TextStyle(fontSize: 30),
),
...widget.infoBlocks.map((block) {
return Text(
'${block.title}: ${block.description}',
style: const TextStyle(fontSize: 16),
);
}),
],
)
],
),
);
}
}
And then you can use it like-so:
Column(
children: [
MyCard(
title: 'Quantity',
infoBlocks: [
InfoBlock(title: 'Price', description: '100'),
],
),
MyCard(
title: 'Status',
infoBlocks: [
InfoBlock(title: 'Txt1', description: 'value1'),
InfoBlock(title: 'Txt2', description: 'value2'),
InfoBlock(title: 'Txt3', description: 'value3'),
],
),
],
));
And here's what the output would look like:
Here's a a link to Flutlab with the above code fully functional: https://flutlab.io/ide/6d8762de-50b1-4cfe-a509-a301edaacebf

How to expand list items without affecting other items. (flutter)

I want to create the Netflix home page in flutter, and I'm stuck while creating this hover state.
I have created two base widgets. One for the number plus the thumbnail and the other one for the expanded view when the widget is hovered. Then I put them in a stack with an Inkwell where the onHover changes the state to show the expanded widget.
When I hover on the widget, it does switch between the normal state an expanded state, the problem comes when I try to put a list of these widgets together.
When using row (or ListView) to put them together, after hovering, the expanded widget makes the other widgets move. (which is not the wanted behaviour, I want them to overlap)
When I use it with stack, the widgets do overlap but now it isn't scrollable anymore.
I have added the link to the repo for anyone that wants to clone it and try running it themselves, I'm running it on flutter web.
https://github.com/Advait1306/netflix-flutter
Widget with thumbnail and number:
class TopListItem extends StatelessWidget {
final int index;
const TopListItem({Key? key, required this.index}) : super(key: key);
#override
Widget build(BuildContext context) {
const double height = 250;
return SizedBox(
height: height,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset("assets/numbers/$index.svg",
fit: BoxFit.fitHeight, height: height),
Transform.translate(
offset: const Offset(-30, 0),
child: Image.asset("assets/thumbnails/thumb1.jpg"))
],
),
);
}
}
Expanded view widget:
import 'package:flutter/material.dart';
class HoverMovieTrailer extends StatelessWidget {
const HoverMovieTrailer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
const textTheme = TextStyle(color: Colors.white);
return SizedBox(
width: 400,
height: 400,
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: const Color(0xFF242424)),
child: Column(
children: [
Image.asset("assets/backgrounds/background1.jpg"),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: const [
RoundIconButton(icon: Icons.play_arrow_outlined),
SizedBox(width: 5),
RoundIconButton(icon: Icons.add_outlined),
SizedBox(width: 5),
RoundIconButton(icon: Icons.thumb_up_alt_outlined),
SizedBox(width: 5),
],
),
Row(
children: const [
RoundIconButton(icon: Icons.keyboard_arrow_down_outlined),
],
),
],
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"98% Match",
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold
),
),
const SizedBox(width: 5),
Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 1)
),
child: const Text(
"18+",
style: textTheme,
),
),
const SizedBox(width: 5),
const Text(
"4 Seasons",
style: textTheme,
),
const SizedBox(width: 5),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 1)
),
child: const Text(
"HD",
style: textTheme,
),
)
],
),
),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"Captivating",
style: textTheme,
),
const SizedBox(width: 5),
Container(
width: 5,
height: 5,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white54
),
),
const SizedBox(width: 5),
const Text(
"Exciting",
style: textTheme,
),
const SizedBox(width: 5),
Container(
width: 5,
height: 5,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white54
),
),
const SizedBox(width: 5),
const Text(
"Docuseries",
style: textTheme,
),
],
),
),
],
),
),
);
}
}
class RoundIconButton extends StatelessWidget {
final IconData icon;
const RoundIconButton({Key? key, required this.icon}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(width: 2, color: Colors.white)),
margin: const EdgeInsets.all(1),
child: IconButton(
onPressed: () {},
icon: Icon(icon),
color: Colors.white,
),
);
}
}
Combining the widgets in the single widget:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:netflix_flutter/widgets/hover_movie_trailer.dart';
import 'package:netflix_flutter/widgets/top_list_item.dart';
class TopListItemWithHover extends StatefulWidget {
const TopListItemWithHover({Key? key}) : super(key: key);
#override
State<TopListItemWithHover> createState() => _TopListItemWithHoverState();
}
class _TopListItemWithHoverState extends State<TopListItemWithHover> {
bool hover = false;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: (){},
onHover: (value){
log("Hover value: $value");
setState(() {
hover = value;
});
},
child: Stack(
clipBehavior: Clip.none,
children: [
TopListItem(index: 1),
if(hover) HoverMovieTrailer(),
],
),
);
}
}
Lists:
import 'package:flutter/material.dart';
import 'package:netflix_flutter/widgets/hover_movie_trailer.dart';
import 'package:netflix_flutter/widgets/top_list_item.dart';
import 'package:netflix_flutter/widgets/top_list_item_with_hover.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
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> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
height: 400,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
itemCount: 8,
itemBuilder: (context, index) {
return TopListItemWithHover();
},
),
),
),
const SizedBox(height: 50),
SingleChildScrollView(
child: SizedBox(
height: 400,
child: Stack(
fit: StackFit.passthrough,
children: [
for (var i = 10; i >= 0; i--)
Positioned(
left: (i) * 300,
child: TopListItemWithHover(),
)
],
),
),
)
],
),
);
}
}
So finally found the solution to this problem, the way to move forward is to use a stack in SingleChildScrollView.
A mistake that I made is, I did not set the SingleChildScrollView's direction to horizontal. So I added that.
And then one more addition that's needed is -- A empty sized box which has the width of sum of all the items in the stack.
Stack(
clipBehavior: Clip.none,
children: [
const SizedBox(
width: 300*10,
),
for (var i = 10; i >= 0; i--)
Positioned(
left: (i) * 300,
child: TopListItemWithHover(),
)
],
)
This finally expanded the stack to the required width and then made is scrollable also.

How to add shadow to ClipOval in flutter?

I have been trying to make a new app being a beginner. So, adding shadows to things is completely new to me.
So, Following is my code:
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
ClipOval(
child: Material(
color: Colors.white, // button color
child: InkWell(
// splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 46, height: 46, child: Icon(Icons.menu,color: Colors.red,),),
onTap: () {},
),
),
),
],
),
),
Following is the mock:
Adding shadow to ClipOval:
Center(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.green,
blurRadius: 50.0,
spreadRadius: 10.0,
)
],
),
child: ClipOval(
child: Image.network(
'https://i.picsum.photos/id/384/536/354.jpg?hmac=MCKw0mm4RrI3IrF4QicN8divENQ0QthnQp9PVjCGblo',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
),
Output:
You can create your own CustomClipper
class CustomClipperOval extends CustomClipper<Rect> {
#override
Rect getClip(Size size) {
return Rect.fromCircle(
center: new Offset(size.width / 2, size.width / 2),
radius: size.width / 2 + 3);
}
#override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return false;
}
}
class ClipOvalShadow extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Rect> clipper;
final Widget child;
ClipOvalShadow({
#required this.shadow,
#required this.clipper,
#required this.child,
});
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: _ClipOvalShadowPainter(
clipper: this.clipper,
shadow: this.shadow,
),
child: ClipRect(child: child, clipper: this.clipper),
);
}
}
class _ClipOvalShadowPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<Rect> clipper;
_ClipOvalShadowPainter({#required this.shadow, #required this.clipper});
#override
void paint(Canvas canvas, Size size) {
var paint = shadow.toPaint();
var clipRect = clipper.getClip(size).shift(Offset(0, 0));
canvas.drawOval(clipRect, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
And then to use it
ClipOvalShadow(
shadow: Shadow(
color: Colors.amber,
offset: Offset(1.0, 1.0),
blurRadius: 2,
),
clipper: CustomClipperOval(),
child: ClipOval(
child: Material(
color: Colors.white, // button color
child: InkWell(
// splashColor: Colors.red, // inkwell color
child: Container(
width: 46,
height: 46,
child: Icon(
Icons.menu,
color: Colors.black,
),
),
onTap: () {},
),
),
),
),
The Result will be