How to create custom widget in Flutter? - flutter

I need to create a custom widget. In the image there is two circles and in the circles there must be some other widgets.
I tried to use Stack and pray for it to be responsive but it didn't. Any ideas?

Custom widget in Flutter is built for Widget Reusability in App. Custom widgets in Flutter can be of many types Widgets are built to make your code clean and Shorter. It makes the Customization of Code Simple and Easy
// code
** Simple Button Custom Widget **
import 'package:flutter/material.dart';
class button extends StatelessWidget {
button(
{super.key,
required this.buttonfun,
required this.iconData,
required this.textdata});
IconData iconData;
String textdata;
VoidCallback buttonfun;
#override
Widget build(BuildContext context) {
return ElevatedButton.icon(
onPressed: buttonfun, icon: Icon(iconData), label: Text(textdata));
}
}

Try using BoxShape.circle,
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
border: Border.all(width: 3),
shape: BoxShape.circle,
// You can use like this way or like the below line
//borderRadius: new BorderRadius.circular(30.0),
color: Colors.amber,
),
child:Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('ABC'),
Text('XYZ'),
Text('LOL'),
],
),
),
To achieve this in general I would recommend using a Column and then 3 Containers, one for each Circle, and something that draws the in-between part.

Related

How to control size of a Column inside a SizedBox

I am building a widget to display a chart/graph inside a fixed size window. The chart will be bigger than the window so the solution includes the user being able to scroll the graph widgets around inside a window (you can find details about this in an earlier question I asked about this here).
Part of the layout includes a fixed sized panel created using a SizedBox and, within that, a Column containing rows of widgets that make up the graph. I need the Column to fit its contents tightly so that I can track it's size and, for example, stop the user scrolling up when the last row is visible at the bottom of the SizedBox.
When the size of the children in the Column should make the Column smaller than the SizedBox, the Column is still being forced to be the size of the SizedBox. This is explained in the Flutter documentation here.
According to the Flutter documentation, the solution is:
This can be remedied by wrapping the child SizedBox in a widget that
does permit it to be any size up to the size of the parent, such as
Center or Align.
I have tried this and it doesn't seem to work. Below is a test app I wrote on DartPad to check this out. If I use either Align or Center as child of SizedBox and parent of the Column widget, the Column is still the same size as the SizedBox. I have also added MainAxisSize.min to the Column, but this doesn't appear to make any difference.
I have considered doing this using a Stack so that the Column is displayed over the SizedBox, rather than as a child of it, but that feels like a bit of a hacky workaround given that the documentation suggests you can control the size of a Column inside a SizedBox.
Does anyone know how I force the Column to be the smallest size it can be inside a SizedBox of fixed size?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
List<Widget> graphWidgets = const [
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: SizedBox(
// This is the window the chart is displayed in
height: 200,
width: 400,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(),
),
child: OverflowBox(
// Used to prevent graph rows from wrapping in the window
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topLeft,
child: DecoratedBox(
// Debug widget to show extent of child column
decoration: BoxDecoration(
border: Border.all(),
color: Colors.amber,
),
child: Center(
// This should allow the Column to be smaller than the SizedBox?
child: Column(
// This holds the widgets that make up the graph
mainAxisSize: MainAxisSize.min,
children: graphWidgets,
),
),
),
),
),
),
),
),
);
}
}
As per SayyidJ's reply, the answer is to use a ConstrainedBox rather than a SizedBox. The Column can shrink to fit the contents when it is the child of a ConstrainedBox.
Here is the revised code, which you can run in DartPad, showing this working.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
List<Widget> graphWidgets = const [
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
Text(
'long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text-long-line-of-text'),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ConstrainedBox(
// This is the window the chart is displayed in
constraints: BoxConstraints(
maxHeight: 200,
maxWidth: 400,
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(),
),
child: OverflowBox(
// Used to prevent graph rows from wrapping in the window
maxHeight: double.infinity,
maxWidth: double.infinity,
alignment: Alignment.topLeft,
child: DecoratedBox(
// Debug widget to show extent of child column
decoration: BoxDecoration(
border: Border.all(),
color: Colors.amber,
),
child: Column(
// This holds the widgets that make up the graph
mainAxisSize: MainAxisSize.min,
children: graphWidgets,
),
),
),
),
),
),
),
);
}
}

How to fix a widget to the right side of a ListView on Flutter Web

I have a Flutter Web application where I need to show a widget on the right side of a ListView when I click an item and this widget should always be visible on screen. I can achieve my objective puting both on a Row and using a scrollable only for the ListView, but that requires the ListView to be wrapped by a widget with defined height.
Defining a container with height to wrap the ListView breaks the responsiveness when I resize the browser, as the container doesn't fit the height of the screen.
I thought of using the shrinkWrap property of the ListView so I don't have to wrap it in a widget with predefined height, but that makes the whole Row scrollable vertically, eventually causing the widget to leave the viewport.
I would appreciate if somebody knows how could I keep this right side widget fixed on screen so I can achieve my objective without losing responsiveness.
Here's something similitar to what I've got so far:
class PageLayout extends StatefulWidget {
const PageLayout({Key? key, required this.items}) : super(key: key);
final List<String> items;
#override
State<PageLayout> createState() => _PageLayoutState();
}
class _PageLayoutState extends State<PageLayout> {
final rightSideWidget = Container(
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(color: Colors.white, width: 2),
),
height: 200);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) => Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.white, width: 2),
),
height: 200,
child: Center(
child: Text(
widget.items[index],
style: const TextStyle(color: Colors.white),
),
),
),
itemCount: widget.items.length,
),
),
Expanded(child: rightSideWidget),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
I want rightSideWidget to be always centered on screen or follow the scroll.
You can divide your screen into two sections, right section and left section; thereby being able to control behaviour of widgets in both sections.
Divide the overall screen into 2 proportional sections using a Row
widget
Put this Row widget inside a Container with height equal to screen height for preserving responsiveness | Use MediaQuery to get current height of page
Now left hand section can individually scroll, and on click of any option from this section you can define behaviour for right section; while keeping the left section constant throughout page lifecycle

Container color almost transparent in BoxDecoration with a predefined color function

The following code works fine to get the right container color. However, when I want to use the _green function replacing Color(0xff0c9869) in the BoxDecoration widget, the color turns out to be very pale/transparent.
It seems like the same solution, but the result is totally different, anyone knows why?
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
final _green = Color(0xff0c9869);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
Container(
height: 200,
decoration: BoxDecoration(
color: Color(0xff0c9869),
borderRadius: BorderRadius.all(
Radius.circular(35),
),
),
child: Row(
children: [Text('Hi'), Icon(Icons.account_circle)],
),
)
]));
}
}

Animate two containers width using Animated container

I have two widgets,
Widget FirstWidget()
{
return Container(
height: 40,
child: new Text('Food Products',style: TextStyle(color: Colors.gray))
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.gray)),
padding: new EdgeInsets.all(12.0),
)
}
Widget SecondWidget()
{
return Container(
height: 40,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Food Products',
TextStyle(color: Colors.blue),
SizedBox(width:8),
Icon(Icons.check,color:Colors.blue ,size: 24,)
],),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.red)),
padding: new EdgeInsets.all(12.0),
)
}
Now, i need to animate the two widgets based on selection,
class BasicProduct extends StatefulWidget {
final String name;
final bool isSelected;
BasicProduct({Key key, #required this.name, this.isSelected = false});
#override
State<StatefulWidget> createState() => _BasicProduct(name, isSelected);
}
class _BasicProduct extends State<BasicProduct> {
final String name;
bool _isSelected;
_BasicProduct(this.name, this._isSelected);
#override
Widget build(BuildContext context) {
Widget _myAnimatedWidget = _isSelected ? SecondWidget() : FirstWidget();
return new GestureDetector(
onTap: () {
setState(() {
_isSelected = !_isSelected;
_myAnimatedWidget =
_isSelected
? SecondWidget()
: FirstWidget();
});
},
child: AnimatedContainer(
key : _animatedContainerKey
duration: const Duration(seconds: 1),
width: boxWidth
child: _myAnimatedWidget
);
}
}
How to get the width for the two widgets to animate?
Tried Calling WidgetsBinding.instance.addPostFrameCallback((_)=> getSize) in the initState to get the Size
getSize()
{
var boxWidth = _animatedContainerKey.currentContext.findRenderObject().size.width;
}
This Size returning for the SecondWidget only, when firstWidget is changed, the Size returning for SecondWidget only.
Can anyone help how to resolve the issue?
I would take a different approach to this. For one, if you can, you should try using the AnimatedCrossFade widget - it automatically handles both the fade and the size change between two different widgets. If you can make it so that instead of having to specify an exact size in your widgets (always a bad idea) they simply fit to the available space, then this should do what you want. However, without more details of what you're actually trying to accomplish with the animation & size of the widgets, it's a bit difficult to help.
If this doesn't work, then you should consider using an AnimationController and a widget inheriting AnimatedWidget. This way, you have complete control over the build process of your two widgets - you can size, position, and fade them however you want with a stack + opacity or whatever else works best for you.
I would normally write out an example but I think it's a bit beyond the scope of this answer, however you can find out an example of an animated widget here: https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html. If you do need to go down this route and can't figure out, it would help if you could add more details about what it is you're trying to accomplish instead of what you've attempted to do to accomplish it.

Set fontFamily for all buttons in Flutter

Is there a way to set the fontFamily for all buttons in a Flutter app?
I see I can set my fontFamily for my MaterialApp using theme.fontFamily, but I'd like to use a different fontFamily for all my buttons.
I saw there is also a ButtonThemeData, but it seems to be related to colors and shapes only.
I don't want to set my fontFamily explicitly every time I use a button or having to wrap all types of buttons, is there any way to accomplish this?
Thanks!
You should use themes to customize fonts for whole widgets, including buttons : https://flutter.dev/docs/cookbook/design/fonts
Simplest might be to create a helper method that returns a Button configured as you wish.
I recomend creating a custom button that "extends" Flutter MaterialButton or RawMaterialButton. Remember to add buttonText as a paramater too if you want your button to be reusable. Also remember to add TextStyle(fontFamily: 'Raleway') to the Text widget style.
Another option would be to "extend" the Flutter Text widget in the same way as with the button example below, and add your CustomTextWidget as a child to the Flutter MaterialButton widget. I prefer to use both in a combination. CustomButton together with CustomText widget.
import 'package:flutter/material.dart';
class CustomButton extends StatelessWidget {
CustomButton({#required this.onPressed});
final GestureTapCallback onPressed;
#override
Widget build(BuildContext context) {
return RawMaterialButton(
fillColor: Colors.green,
splashColor: Colors.greenAccent,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const <Widget>[
Icon(
Icons.face,
color: Colors.amber,
),
SizedBox(
width: 10.0,
),
Text(
"Tap Me",
maxLines: 1,
style: TextStyle(color: Colors.white),
),
],
),
),
onPressed: onPressed,
shape: const StadiumBorder(),
);
}
}
Here is the implementation:
CustomButton(
onPressed: () {
print("Tapped Me");
},
)