Flutter: how to make BoxShadow overflow sibling widgets? - flutter

I have a Row with two Expanded widgets as its children. There is no space/padding between a children, therefore. Each of the children contains a Container (with box decoration), which in turn contains a FlatButton. When the FlatButton is highlighted, a BoxShadow appears around the Container.
For the second button, the shadow appears just fine, overflowing part of the first button. However, when the first button is being highlighted, the right side of the BoxShadow, which is supposed to be "over" the second button, gets clipped. I assume it's because the second button (and its respective ancestors) are after the first button in the widget tree, thus being "higher".
Here is the DartPad instance I made to demonstrate the issue: https://dartpad.dev/3e1eaa23c2848ada0104bded02e1510a
My question is essentially how I could achieve, with exactly the same layout, ie. no space between the buttons, that the shadow of each button appears over the other one. That is, the shadow of "Button1" in the example is the same as that of "Button2";
Thanks!

Hi You can check the edited code.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Generated App',
theme: new ThemeData(
primarySwatch: Colors.blue,
primaryColor: const Color(0xFF2196f3),
accentColor: const Color(0xFF2196f3),
canvasColor: const Color(0xFFfafafa),
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('App Name'),
),
body: new Container(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: new Row(
children: [
Expanded(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 5, 0),
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.transparent,
blurRadius: 5.0,
),
]),
child: TestButton("Button1")),
),
)),
Expanded(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 5, 0),
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.transparent,
blurRadius: 5.0,
),
]),
child: TestButton("Button2")),
),
))
],
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
))),
padding: const EdgeInsets.all(0.0),
alignment: Alignment.center,
),
);
}
}
class TestButton extends StatefulWidget {
#required
final String title;
TestButton(this.title);
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> {
bool isHighlighted = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
height: 50,
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(isHighlighted ? 0.5 : 0.0),
spreadRadius: isHighlighted ? 5 : 0,
)
]),
child: FlatButton(
shape: ContinuousRectangleBorder(borderRadius: BorderRadius.zero),
splashColor: Colors.black12,
color: Colors.white,
hoverColor: Colors.black12,
highlightColor: Colors.black12,
mouseCursor: MouseCursor.uncontrolled,
child: Text(widget.title),
onPressed: () => null,
onHighlightChanged: (value) {
setState(() {
isHighlighted = value;
});
},
));
}
#override
void dispose() {
super.dispose();
}
}

Related

How to make dropdown list with images and selected item also show with image in flutter?

Tell me how can I make a dropdown list but so that the elements are in the form of cards with an image? It is also necessary that the selected element be displayed without an arrow and with an image.
The following code would do the trick. The DropdownButton accepts DropdownMenuItem as items. And each menu item is customizable with a widget. Just layout an image and the texts and here you go:
Check also the live demo on DartPad
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int? _value = 0;
void _setValue(int? value) {
setState(() {
_value = value;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromRGBO(50, 47, 61, 1),
body: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: const Color.fromRGBO(57, 56, 69, 1),
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(
color: Colors.black26,
spreadRadius: 5,
blurRadius: 12,
offset: Offset(0, 0))
]),
child: DropdownButton(
itemHeight: 124,
icon: const SizedBox.shrink(),
value: _value,
underline: Container(),
dropdownColor: const Color.fromRGBO(57, 56, 69, 1),
onChanged: _setValue,
borderRadius: BorderRadius.circular(12),
items: [
for (var i = 0; i < 10; i++)
DropdownMenuItem(
value: i,
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image(
image: NetworkImage(
'https://source.unsplash.com/random/100x100?sig=$i&cars')),
),
const SizedBox(width: 8),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Test title $i',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Text(
'Test $i',
style: const TextStyle(color: Colors.white),
),
],
),
],
))
],
),
),
),
);
}
}
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
var _img = new Image.network("https://upload.wikimedia.org/wikipedia/commons/thumb/8/89/TUCPamplona10.svg/500px-TUCPamplona10.svg.png");
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Test Drop"),),
body: new Center(
child: new Container (
height: 50.0,
child:new DropdownButton(
items: new List.generate(10, (int index){
return new DropdownMenuItem(child: new Container(
padding: const EdgeInsets.only(bottom: 5.0),
height: 100.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_img,
new Text("Under 10")
],
),
));
})
, onChanged: null),),
),
);
}
}
this gif will give you better idea Demo of Code

Is there any alternative for Stack widget that allow gesture in overflow?

I want to implement this. I could easily achieve it in CSS using position: absolute, a bit of if-else statement in JavaScript, done.
I'm trying to achieve the same thing in Flutter (see code snippet below), I use the Stack widget, it does give the same visual result. But, I cannot click anything inside the options box.
I've done searching for a solution and found that according to the answer of this question, this behaviour is intentional, and I should refactor my code not to use ClipBehavior (Overflow is deprecated). With that being said, I could do something like just using Column instead of Stack but I need the Stack's behaviour where the Options should not push another widgets when it is being shown, similar to position: absolute in CSS.
I am wondering if there is any other widget that do the same thing as Stack but allow me to interact with the Positioned elements/widgets outside of its bound. If there is any, please let me know!
main.dart
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const Scaffold(
backgroundColor: Colors.blueGrey,
body: Center(
child: CustomDropdown(),
),
);
}
}
class CustomDropdown extends StatefulWidget {
const CustomDropdown({Key? key}) : super(key: key);
#override
State<CustomDropdown> createState() => _CustomDropdownState();
}
class _CustomDropdownState extends State<CustomDropdown> {
bool showOptions = false;
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
_buildPrimaryButton(),
if (showOptions) _buildOptions(),
],
);
}
Widget _buildPrimaryButton() {
return Ink(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.grey.shade200),
borderRadius: BorderRadius.circular(10),
),
child: InkWell(
onTap: () {
setState(() {
showOptions = !showOptions;
});
},
child: const Padding(
padding: EdgeInsets.all(10),
child: Text('Primary Button'),
),
),
);
}
Widget _buildOptions() {
return Positioned(
right: 0,
bottom: -145,
child: Ink(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.grey.shade200),
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () => print('TAPPED Option 1'),
child: const Padding(
padding: EdgeInsets.fromLTRB(20, 10, 150, 10),
child: Text('Option 1'),
),
),
InkWell(
onTap: () => print('TAPPED Option 2'),
child: const Padding(
padding: EdgeInsets.fromLTRB(20, 10, 150, 10),
child: Text('Option 2'),
),
),
InkWell(
onTap: () => print('TAPPED Option 3'),
child: const Padding(
padding: EdgeInsets.fromLTRB(20, 10, 150, 10),
child: Text('Option 3'),
),
),
],
),
),
);
}
}

How to add highlight overlay around the widget upon click?

I'm working on simple design system using Flutter. I want to highlight a widget upon selection (click), as you can see in the below images button get highlighted upon click. It gets handle and border.
Challenging part: I don't want layout getting changed as additional space taken by handle and border upon click. I want widget, handle and border are overlaid, so that it wouldn't shift the position of other neighbouring widgets.
And after selection
You could also use a Stack with the overlay bleeding out of the Stack thanks to a clipBehavior of Clip.none.
Full code
Just copy paste it in a DartPad to see it in action.
import 'package:flutter/material.dart';
const kcPrimary = Color(0xFF001989);
const kcSecondary = Color(0xFF239689);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Text('Blablablablabla'),
Text('Blablablablabla'),
Text('Blablablablabla'),
PlayButton(),
Text('Blablablablabla'),
Text('Blablablablabla'),
Text('Blablablablabla'),
],
),
),
),
);
}
}
class PlayButton extends StatefulWidget {
const PlayButton({Key? key}) : super(key: key);
#override
State<PlayButton> createState() => _PlayButtonState();
}
class _PlayButtonState extends State<PlayButton> {
bool clicked = false;
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
InkWell(
onTap: () => setState(() => clicked = !clicked),
child: _mainButton,
),
if (clicked) ...[
Positioned.fill(
child: IgnorePointer(
child: _overlayBorder,
),
),
Positioned(
top: -20.0,
left: 0,
child: _overlayTitle,
),
Positioned(top: 0, right: 0, child: _corner),
Positioned(bottom: 0, right: 0, child: _corner),
Positioned(bottom: 0, left: 0, child: _corner),
Positioned(top: 0, left: 0, child: _corner),
],
],
);
}
Widget get _mainButton => Container(
width: 80.0,
height: 40.0,
decoration: BoxDecoration(
border: Border.all(
color: kcPrimary,
width: 3.0,
),
borderRadius: const BorderRadius.all(Radius.circular(12))),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(Icons.play_arrow),
Text('Play'),
],
),
),
);
Widget get _overlayBorder => Container(
decoration: BoxDecoration(
border: Border.all(
color: kcSecondary,
width: 3.0,
),
),
);
Widget get _corner => Container(width: 10, height: 10, color: kcSecondary);
Widget get _overlayTitle => Container(
height: 20.0,
width: 48.0,
color: kcSecondary,
alignment: Alignment.center,
child: const Text(
'Button',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
);
}

How to stop positioned text objects from moving inside Stack as text changes across devices?

Objective: I want a TextButton to be bisected by a Container in a Stack.
Problem: The size of the TextButton changes across devices, causing an unwanted offset. See the first photo.
Below is a minimum reproducible example in which I use a static integer offset to position the TextButton. I also attempted to specify the TextButton's parent Container's width and height in an attempt to ensure my positional offset was the same each time, but overconstraining the TextButton caused odd reactions like this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: '):',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: '):'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool buttonToggled = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: EdgeInsets.fromLTRB(0, 30, 0, 0),
child: Stack(
clipBehavior: Clip.hardEdge,
alignment: AlignmentDirectional.center,
children: <Widget>[
Container(
//outline
height: 135,
margin: EdgeInsets.fromLTRB(20, 20, 20, 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.purpleAccent, width: 1),
borderRadius: BorderRadius.circular(5),
shape: BoxShape.rectangle,
color: Colors.green,
),
),
Positioned(
left: 40,
top: 3,
child: Container(
margin: EdgeInsets.fromLTRB(10, 0, 0, 0),
padding: EdgeInsets.fromLTRB(7, 0, 7, 0),
height: 30,
color: Colors.blue,
child: Center(
child: TextButton(
child: Text(
buttonToggled ? '+ End Date and Time ' : '— End Date and Time',
textAlign: TextAlign.start, //aligment
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
buttonToggled = !buttonToggled;
});
},
)))),
],
),
));
}
}
if you setup same size, inset, margin, it should not be different in different device.
have a try with TextStyle(color: Colors.white, inherit: false)

How to prevent Flutter from clipping BoxDecoration shadows?

Here's a screenshot of a simple flutter app:
Pushing the ++/-- buttons adds/removes a panel to/from a Container. The container is outlined with a blue shadow.
Now, as soon as the container grows too close to the bottom border, it's shadow gets clipped on the left and right border.
Do you have any ideas what causes the clipping and how to avoid it?
This is how the code looks:
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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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> {
int _counter = 0;
ScrollController _scrollController;
#override
void initState() {
_scrollController = ScrollController();
super.initState();
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
if (_counter > 0) {
_counter--;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20),
child: ListView(
controller: _scrollController,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 30),
_getButtonPanel(),
SizedBox(height: 20),
Container(
padding: EdgeInsets.all(20),
child: _getChildren(_counter),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.blue,
offset: Offset(0, 0),
blurRadius: 10.0,
spreadRadius: 5.0,
)
],
),
),
SizedBox(height: 20),
],
),
],
),
),
);
}
Widget _getButtonPanel() {
return Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
OutlineButton(
onPressed: _decrementCounter,
child: Text('--'),
),
Spacer(),
OutlineButton(
onPressed: _incrementCounter,
child: Text('++'),
),
],
);
}
Widget _getChildren(int cnt) {
List<Widget> children = [];
for (int i = 0; i < cnt; i++) {
children.add(_getItem(1 + i));
}
return Column(
children: children,
);
}
Widget _getItem(int i) {
return Column(
children: <Widget>[
SizedBox(
height: 50,
),
Text('Child panel $i'),
SizedBox(
height: 50,
),
],
);
}
}
You should add margin to the container containing the list to prevent it from clipping.
Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(15), //Add margin
child: _getChildren(_counter),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Colors.blue,
offset: Offset(0, 0),
blurRadius: 10.0,
spreadRadius: 5.0,
)
],
),
);