Adjust Width from the Left Side of Container - flutter

Changing the width of a container defaults to resizing from the right side.
The pseudocode below has an example where dx is a variable that can change. When it increases or decreases, the container will always grow or shrink from the right side.
Is there a simple way to switch the direction so that the width will increase or decrease from the left side instead of the right side?
Container(
width: dx,
height:200
)
Here is a dartpad gist that shows how the right side of the container's width changes when dragged. What I'm asking is if there is a quick and simple way to make the left side expand/contract without having to animate the position of the container: https://dartpad.dev/?id=ebbe57041bf950018fe5733674c68b20

I checked out your dartpad code. To achieve what you want, I suggest you put two empty Containers on either side of your handles and decrease their size when the handles are dragged (your center Container should also be inside an Expanded widget to take up all the allowed space). here is the example code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
// Application name
title: 'Flutter Stateful Clicker Counter',
theme: ThemeData(
// Application theme data, you can set the colors for the application as
// you want
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Clicker Counter Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double? rightContainerWidth, leftContainerWidth;
#override
Widget build(BuildContext context) {
rightContainerWidth ??= MediaQuery.of(context).size.width / 2 - 20;
leftContainerWidth ??= MediaQuery.of(context).size.width / 2 - 20;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Wrap(
children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
// left handle
Container(
width: leftContainerWidth,
),
GestureDetector(
onHorizontalDragUpdate: (DragUpdateDetails details) {
setState(() {
leftContainerWidth = details.globalPosition.dx;
});
},
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
bottomLeft: Radius.circular(20),
)),
width: 10,
height: 200)),
Expanded(
child: Container(
// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: ClipRect(
child: Container(
// width: _counter+0.2,
height: 200,
color: Colors.green,
))),
),
GestureDetector(
onHorizontalDragStart: (DragStartDetails details) {
print("st: ${details.localPosition.dx}");
// dx for start is the x offset of the mouse relative to the container
// changeX = (_counter as double) - details.localPosition.dx.floor();
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
setState(() {
// print(details.localPosition.dx);
rightContainerWidth = MediaQuery.of(context).size.width -
details.globalPosition.dx;
});
},
child: Container(
width: 10,
height: 200,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
bottomRight: Radius.circular(20),
)))),
Container(
width: rightContainerWidth,
),
])
],
),
),
);
}
}
Caution: I did not add conditional statements to prevent overflows, make sure you also add them!

Use Wrap Widget
A widget that displays its children in multiple horizontal or vertical runs.
A Wrap lays out each child and attempts to place the child adjacent to the previous child in the main axis, given by direction, leaving spacing space in between. If there is not enough space to fit the child, Wrap creates a new run adjacent to the existing children in the cross axis.
After all the children have been allocated to runs, the children within the runs are positioned according to the alignment in the main axis and according to the crossAxisAlignment in the cross axis.
The runs themselves are then positioned in the cross axis according to the runSpacing and runAlignment.
Example:
Wrap(
spacing: 8.0, // gap between adjacent chips
runSpacing: 4.0, // gap between lines
children: <Widget>[
Chip(
avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('AH')),
label: const Text('Hamilton'),
),
Chip(
avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('ML')),
label: const Text('Lafayette'),
),
Chip(
avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('HM')),
label: const Text('Mulligan'),
),
Chip(
avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: const Text('JL')),
label: const Text('Laurens'),
),
],
)

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

Custom floating bottom navigation bar has white background flutter

I am trying to create a custom floating bottom navigation bar and i create a widget and added a margin to create a floating effect but it adds a white background.
I need to create it without the white background.
Here is my code;
Scaffold(
bottomNavigationBar: AnimatedBottomBar(
currentIcon: viewModel.currentIndex,
onTap: (int index) => viewModel.updateIndex(index),
icons: viewModel.icons,
),
body: viewModel.pages[viewModel.currentIndex],
);
Then the animated bottom bar
import 'package:flutter/material.dart';
import 'package:woneserve_updated_mobile_app/app/theming/colors.dart';
import 'package:woneserve_updated_mobile_app/models/icon_model.dart';
class AnimatedBottomBar extends StatelessWidget {
final int currentIcon;
final List<IconModel> icons;
final ValueChanged<int>? onTap;
const AnimatedBottomBar({
Key? key,
required this.currentIcon,
required this.onTap,
required this.icons,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
child: Container(
margin: const EdgeInsets.all(40),
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: const Offset(0, 2), // changes position of shadow
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: icons
.map(
(icon) => GestureDetector(
onTap: () => onTap?.call(icon.id),
child: AnimatedSize(
duration: const Duration(milliseconds: 900),
child: Icon(
icon.icon,
size: currentIcon == icon.id ? 26 : 23,
color: currentIcon == icon.id ? primaryColor : Colors.black,
),
),
),
)
.toList(),
),
),
);
}
}
How can i create the same effect without the white background? Any help would be appreciated.
You might need to add extendBody: true in Scaffold.
my friend
for solve this problem you have 3 way.
use extendBody: true in your Scaffold.
use theme in MaterialApp Widget.(see below)
ThemeData(
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: Colors.transparent,
),
),
use floatingActionButton instead of bottomNavigationBar in Scaffold.(see below)
Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: AnimatedBottomBar(...),
...
)
Try remove first Container in build method. If not work, remove margin and padding in second Container.

Do not want rounded corners in the AppBar when the Sliver App Bar is collapsed

I'm trying to implement a layout, where the Sliver App Bar has rounded bottom corners when expanded, but when it is collapsed I do not want those rounded corners.
Actual Behaviour:
enter image description here
Expected Behaviour:
Here's my SliverAppBar code:
`SliverAppBar(
systemOverlayStyle: const SystemUiOverlayStyle(
statusBarColor: Color(0xFFE0E64B),
),
backgroundColor: Color(0xFFE0E64B),
expandedHeight: 300.0,
floating: false,
pinned: true,
collapsedHeight: 60.0,
onStretchTrigger: () async {
setState(() {});
},
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text(
'Pokedex',
style: TextStyle(
color: Colors.white,
),
),
Text(
'#025',
style: TextStyle(
color: Colors.white,
),
),
],
),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
background: Container(
decoration: const BoxDecoration(
color: Color(0xFFE0E64B),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(50.0),
bottomRight: Radius.circular(50.0),
),
),
child: Hero(
tag: 'pokemon_container$index',
child: Column(
children: [
const SizedBox(
height: 120.0,
),
Expanded(
child: ClipRRect(
child: Image.network(
imageUrl,
fit: BoxFit.scaleDown,
),
),
),
const SizedBox(
height: 30.0,
),
],
),
),
),
),
),`
shape: ContinuousRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))),
Here is your code. Put it inside sliverAppBar
NestedScrollView / SliverAppBar solution
This is definitely achievable. SliverAppBar does support what we need, it has support for rounded borders, the shadow effect and changing sizes. For handling the border requirement we can use a RoundedRectangleBorder.
Although for getting a smooth transition for the border change, we need to update the values frequently, when changing the size of the SliverAppBar.
Example code
Do note that the package flutter_riverpod (version 1.0.3) is used for state management in this example.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class RoundedSliverExampleScreen extends StatelessWidget {
const RoundedSliverExampleScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
ExpandingAppBar(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Flexible is important for the children widgets added here.
Flexible(child: Container(color: Colors.yellow, width: 50, height: 50,))
],
)
];
},
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text("Hello!")
],
),
)
);
}
}
/// An SliverAppBar widget with alternating rounded border depending on the
/// expandedHeight.
///
/// Provides easy support for adding children widgets in the
/// expanded area as if it was a Column, although children widgets should be
/// wrapped in a Flexible widget.
class ExpandingAppBar extends ConsumerWidget {
const ExpandingAppBar({
Key? key,
this.children = const <Widget>[],
this.mainAxisAlignment = MainAxisAlignment.start
}) : super(key: key);
final List<Widget> children;
final MainAxisAlignment mainAxisAlignment;
#override
Widget build(BuildContext context, WidgetRef ref) {
RoundedHeaderState state = ref.watch(roundedHeaderProvider);
return SliverAppBar(
expandedHeight: state.highestHeight,
pinned: true,
primary: true,
forceElevated: true,
title: const Text('Pokèdex'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(state.radius)),
),
flexibleSpace: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// We update the state here.
ref.read(roundedHeaderProvider.notifier).updateHeight(constraints.maxHeight);
return Opacity(
opacity: state.scrollFraction,
child: Padding(
padding: EdgeInsets.only(top: state.smallestHeight),
child: Column(mainAxisAlignment: mainAxisAlignment, children: children),
),
);
},
),
);
}
}
#immutable
class RoundedHeaderState {
final double highestHeight = 256;
final double smallestHeight = kToolbarHeight + 24;
final double currentHeight;
final double contentOpacity = 1;
const RoundedHeaderState({this.currentHeight = 256});
double get scrollFraction => min(max((currentHeight - smallestHeight) / (highestHeight - smallestHeight), 0), 1);
double get radius => 64 * scrollFraction;
}
class RoundedHeaderNotifier extends StateNotifier<RoundedHeaderState> {
RoundedHeaderNotifier(): super(const RoundedHeaderState());
updateHeight(double currentHeight) {
final newState = RoundedHeaderState(currentHeight: currentHeight);
// Check that the new state is not equal to the next (prevents rebuild loop)
if(state.currentHeight != newState.currentHeight) {
// Setting state triggers an rebuild, the PostFrameCallback let Flutter
// postpone the upcoming rebuild at a later time.
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
state = newState;
});
}
}
}
final roundedHeaderProvider = StateNotifierProvider<RoundedHeaderNotifier, RoundedHeaderState>((ref) {
return RoundedHeaderNotifier();
});
// Pay attention to the ProviderScope wrapping the MaterialApp. Riverpod requires this.
void main() => runApp(
const ProviderScope(
child: MaterialApp(home: RoundedSliverExampleScreen())
)
);
Result - Gif of the SliverAppBar's transition.

ClipRRect doesn't clip it's child widget, what am I doing wrong?

I am clipping an image with ClipRRect, it initially worked, I changed nothing of consequence in the code and the image still displays, but the image no longer clips. This is the code, please tell me what is wrong.
ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: InkWell(
child: Image(
image: AssetImage(
_images[index]['image'],
),
fit: BoxFit.contain,
),
onTap: () =>
Navigator.pushNamed(context, _images[index]['route'])),
),
Works for me fine in dartpad.dev try reinstalling your app
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: InkWell(
child:Image.network('https://picsum.photos/250?image=9',fit: BoxFit.contain,),
onTap: () => null),
),
);
}
}
you can check this for more also
https://educity.app/flutter/how-to-clip-images-in-flutter
and mainly the problem is in BoxFit.contain cahnge it to BoxFit.cover cause that make the parent choose one of the dimensions which makes the other not clipped.
you can also try specifying image width and height this will work also ,
and finally you can try
CircleAvatar( radius: 20, backgroundImage:NetworkImage('via.placeholder.com/140x100') )
Try this code, It perfectly works for me
ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: InkWell(
child: Image.asset(
_images[index]['image'],
fit: BoxFit.contain,
),
onTap: () =>
Navigator.pushNamed(context, _images[index]['route'])),
),
),
change fit: BoxFit.contain to cover:
ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: InkWell(
child: Image(
image: AssetImage(
_images[index]['image'],
),
fit: BoxFit.cover,
),
onTap: () =>
Navigator.pushNamed(context, _images[index]['route'])),
),
I had a similar issue where ClipOval and CircleAvatar was not clipping a image.
Pay attention to the final widget size (which is NOT obvious).
For instance, this code won't work when placed in the actions of an AppBar:
SizedBox(
width: size,
height: size,
child: ClipOval(
clipBehavior: Clip.antiAliasWithSaveLayer,
child: SizedBox(
width: size,
height: size,
child: userProfile.photoURL.startsWith("http")
? CachedNetworkImage(
userProfile.photoURL,
width: iSize,
height: iSize,
)
: Image.file(
File(userProfile.photoURL),
cacheWidth: siSize,
cacheHeight: siSize,
scale: mediaQuery.devicePixelRatio,
),
),
),
)
because the AppBar has 56 pixels high, the SizedBox will have 56 pixels high (no matter if you specify the height). To check this, insert all widgets inside a Container(color: Colors.red) and, if you see true widget size in red.
For the above example, you would need to remove the inner SizedBox (as is redundant) and insert all that tree inside a Center, so, no matter the available size, it will obey the SizedBox size and center the content.

Flutter: How to create bidirectional scrolling ListView with fixed portion of on left and bottom

How can I make a scrolling view in Flutter in which a left and bottom portion of the screen is fixed (axes), then the rest of the screen can be scrolled horizontally to the right, or vertically upward. (imagine scrolling a graph with two axes .. see image)
Very interesting question. After looking through the docs I couldn't find a widget that would fit this scenario. So I decided to search a bit on pub.dev for a plugin that could make this happen.
Found it: https://pub.dev/packages/bidirectional_scroll_view
The plugin does a fairly good job of scrolling content on both axis, but to get what you are looking for ("fixed portion of on left and bottom") you are gonna have to structure your page accordingly. I decided to go with Stack and Align widgets, here is what it looks like:
See the full code on a working DartPad: https://dartpad.dev/10573c0e9bfa7f1f8212326b795d8628
Or take a look at the code bellow (don't forget to include bidirectional_scroll_view in your project):
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
BidirectionalScrollViewPlugin _plugin;
double fixedAxisSpace = 100.0;
double biDirectContentWidth = 4096.0;
double biDirectContentHeight = 4096.0;
#override
void initState() {
super.initState();
_plugin = new BidirectionalScrollViewPlugin(
child: _buildWidgets(),
velocityFactor: 0.0,
);
}
void _snapToZeroZero(BuildContext context){
double yOffset = biDirectContentHeight + fixedAxisSpace - context.size.height;
_plugin.offset = new Offset(0, yOffset);
}
#override
Widget build(BuildContext context) {
final btnSnapToZeroZero = Padding(
padding: EdgeInsets.all(10.0),
child:FlatButton(
color: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(12.0),
),
onPressed: () { _snapToZeroZero(context); },
child: Text(
"Snap to 0.0",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
)
);
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: new Scaffold(
body: Stack(
children: <Widget>[
_plugin, // BidirectionalScrollViewPlugin, goes 1st because we want it to sit on the bottom layer
Align( // Y Axis goes over _plugin, it is aligned to topLeft
alignment: Alignment.topLeft,
child: Column(
children: <Widget> [
Expanded(
child: Container(
width: fixedAxisSpace,
decoration: BoxDecoration(
color: Colors.white, // change to Colors.white70 too se what is going on "behind the scene"
border: Border(
right: BorderSide(width: 1.0, color: Colors.black),
),
),
child: Center(child: VerticalTextWidget("FIXED _ Y AXIS", 22))
),
),
SizedBox(height: fixedAxisSpace),
]
),
),
Align( // X Axis goes over _plugin and Y Axis, it is aligned to bottomLeft
alignment: Alignment.bottomLeft,
child: Row(
children: <Widget> [
SizedBox(width: fixedAxisSpace),
Expanded(
child: Container(
height: fixedAxisSpace,
decoration: BoxDecoration(
color: Colors.white, // change to Colors.white70 too se what is going on "behind the scene"
border: Border(
top: BorderSide(width: 1.0, color: Colors.black),
),
),
child: Center(child: Text("FIXED | X AXIS", style: TextStyle(fontSize: 22)))
),
),
]
),
),
Align( // this little square is optional, I use it to put a handy little button over everything else at the bottom left corner.
alignment: Alignment.bottomLeft,
child: Container(
color: Colors.white, // change to Colors.white70 too se what is going on "behind the scene"
height: fixedAxisSpace,
width: fixedAxisSpace,
child: btnSnapToZeroZero
),
),
],
)
)
);
}
// put your large bidirectional content here
Widget _buildWidgets() {
return new Padding(
padding: EdgeInsets.fromLTRB(100, 0, 0, 100),
child: SizedBox(
width: biDirectContentWidth,
height: biDirectContentHeight,
child: Image.network(
'https://i.stack.imgur.com/j1ItQ.png?s=328&g=1',
repeat: ImageRepeat.repeat,
alignment: Alignment.bottomLeft
),
)
);
}
}
VerticalTextWidget:
class VerticalTextWidget extends StatelessWidget {
final String text;
final double size;
const VerticalTextWidget(this.text, this.size);
#override
Widget build(BuildContext context) {
return Wrap(
direction: Axis.vertical,
alignment: WrapAlignment.center,
children: text.split("").map((string) => Text(string, style: TextStyle(fontSize: size))).toList(),
);
}
}