So I am trying to create a custom widget in flutter that has multiple constructors.
Essentially I am creating an arrow button but want to be able to create the button with arrow icons facing in different directions.
Currently my code looks like this:
class RotateButton extends StatefulWidget {
RotateButton.left() {
_RotateButtonState createState() => _RotateButtonState.left();
}
RotateButton.right() {
_RotateButtonState createState() => _RotateButtonState.right();
}
#override
_RotateButtonState createState() => _RotateButtonState();
}
class _RotateButtonState extends State<RotateButton> {
IconData icon;
_RotateButtonState();
_RotateButtonState.left() {
icon = Icons.arrow_back;
}
_RotateButtonState.right() {
icon = Icons.arrow_forward;
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Icon(
icon,
size: 70,
),
),
);
}
}
Every time I use my widget it just defaults to the default constructor and shows no Icon child.
Is there a way to build a class without making a default constructor.
Also is there a way I can build this widget without using a stateful widget as it kind of just overcomplicates it.
I am getting a message that says:
The declaration 'createState' isn't referenced
This message is coming up next to the named constructors in the rotatebutton class.
Any help is very much appreciated.
Use something like this:
class SomeWidget extends StatelessWidget {
final bool isRight;
const SomeWidget({Key key, this.isRight}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Icon(isRight?Icons.arrow_forward:Icons.arrow_back),
);
}
}
Related
I have problems following step by step what happens when onChanged is triggered on my TextField. Especially, I have a problem understanding where and why the variable value gets its actual value in the following example.
Example:
class felder extends StatefulWidget {
felder({super.key});
String textFieldName = "";
#override
State<felder> createState() => _felderState();
}
class _felderState extends State<felder> {
#override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
obscureText: false,
decoration: const InputDecoration(
border: OutlineInputBorder(), labelText: 'Name'),
onChanged: (value) => widget.textFieldName = value,
)
],
);
}
}
How I always imagined it: I think flutter passes a function in the background, which has a parameter value, that has the content of the TextField.
Actually TextField is a widget that has its own state.
Whenever user types something, the value in a TextField
changes.
At that time, a callback is fired from the TextField.
The changed value is also passed along with the
callback.
Using onChanged: (value){ print(value); } , we can
get the value from that callback and use it as per our needs.
From TextField source code,
The text field calls the [onChanged] callback whenever the user changes the text in the field. If the user indicates that they are done typing in the field (e.g., by pressing a button on the soft keyboard), the text field calls the [onSubmitted] callback.
To get the value from a TextField, you can also use TexteditingController.
First declare TextEditingController controller = TextEditingController();.
Then inside your TextField, add the controller like this
TextField(
controller: controller,
),
Then to get the value from controller, you can use controller.value.text.
What is a callback?
From GeeksForGeeks:
Callback is basically a function or a method that we pass as an
argument into another function or a method to perform an action. In
the simplest words, we can say that Callback or VoidCallback are used
while sending data from one method to another and vice-versa
Creating a callback
To create your own callback, you can use ValueChanged.
Code example:
Let's create our own button, that when the onChanged is called, it will give us a new value:
class ButtonChange extends StatefulWidget {
final bool value;
final ValueChanged<bool> onChanged;
ButtonChange({Key? key, required this.value, required this.onChanged})
: super(key: key);
#override
State<ButtonChange> createState() => _ButtonChangeState();
}
class _ButtonChangeState extends State<ButtonChange> {
bool _isToggled = false;
void toggle() {
setState(() {
_isToggled = !_isToggled;
});
widget.onChanged(_isToggled);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: toggle,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: _isToggled ? Colors.green : Colors.red,
borderRadius: BorderRadius.circular(50),
),
),
);
}
}
Usage:
class MyWidget extends StatefulWidget {
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
var _value = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
ButtonChange(
value: _value,
onChanged: (bool value) => setState(() {
_value = value;
})),
Text('$_value')
],
);
}
}
Complete example
You can run/paste this example in your editor, and take a look:
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 StatefulWidget {
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
var _value = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
ButtonChange(
value: _value,
onChanged: (bool value) => setState(() {
_value = value;
})),
Text('$_value')
],
);
}
}
class ButtonChange extends StatefulWidget {
final bool value;
final ValueChanged<bool> onChanged;
ButtonChange({Key? key, required this.value, required this.onChanged})
: super(key: key);
#override
State<ButtonChange> createState() => _ButtonChangeState();
}
class _ButtonChangeState extends State<ButtonChange> {
bool _isToggled = false;
void toggle() {
setState(() {
_isToggled = !_isToggled;
});
widget.onChanged(_isToggled);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: toggle,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: _isToggled ? Colors.green : Colors.red,
borderRadius: BorderRadius.circular(50),
),
),
);
}
}
See also
How to pass callback in Flutter
What's in onChanged Docs ?
ValueChanged<String>? onChanged
onChanged is of type ValueChanged<String> and is called when the user initiates a change to the TextField's value: when they have inserted or deleted text.
This callback doesn't run when the TextField's text is changed programmatically, via the TextField's controller. Typically it isn't necessary to be notified of such changes, since they're initiated by the app itself.
What is Callback ?
If we go by definition, the Callback is a function or a method which we pass as an argument into another function or method and can perform an action when we require it.
For Example, if you are working in any app and if you want any change in any value then what would you do?
Here you are in a dilemma that what you want to change either state() or a simple value/values. If you need to change states then you have various state-changing techniques but if you want to change simple values then you will use Callback.
Refer this article to understand the callback on event of textChange this will surely make you understand the core behind the mechanism
I'm working on a big app tightly with the designer.
I was a part of the style guide and the component design process,the designer handed off his work and now its my turn to translate some prototypes and styles to real flutter app.
I'm having a really hard time translating the style guide to flutter widgets.
For example, I have a custom chip widget created from scratch, with many style properties.
There are two main different styles for this chip (normal and highlighted), and the chip is used widely (I believe more than 50 references), excluding other more complex widgets using it.
I tried :
Creating a style class for each big widget, and created extension on ThemeData.
class MyCustomChip extends StatelessWidget {
Widget build(BuildContext context) {
final style = Theme.of(context).myCustomChip;
/** return the custom chip **/
}
}
class MyCustomChipStyle {
/** many style properties **/
}
extension MyCustomChipThemeData on ThemeData {
MyCustomChipStyle get myCustomChip => ...
}
Created a static style class, and passed relevant style properties every time the widget used.
class MyCustomChip extends StatelessWidget {
final Color color;
final BorderRadius borderRadius;
/** many more styling properties **/
}
abstract class MyCustomChipValues {
static const BorderRadius kBorder = /** Getting value from a predefined radiuses **/
static const Color kColor = /** Getting value from a predefined palette **/
/** Many more **/
}
class WidgetUsingTheChip extends StatelessWidget {
Widget build(BuildContext context) {
/** those lines will be duplicated alot **/
return MyCustomChip(
color: MyCustomChipValues.kColor,
borderRadius: MyCustomChipValues.kBorder,
/** Many more **/
);
}
}
Used all the relevant style properties inside the custom widget, and didn't expose them.
abstract class MyCustomChipValues {
static const BorderRadius kBorder = /** Getting value from a predefined radiuses **/
static const Color kColor = /** Getting value from a predefined palette **/
/** Many more **/
}
class MyCustomChip extends StatelessWidget {
Widget build(BuildContext context) {
/** making it virtually impossible to change style depending on state **/
return Container(
color: MyCustomChipValues.kColor,
/** Many more **/
);
}
}
class WidgetUsingTheChip extends StatelessWidget {
Widget build(BuildContext context) {
return MyCustomChip();
}
}
I have defined other style classes :
Insets (various paddings and margins)
Colors (our palette, with buttons, chips, input, scaffold, etc..)
BorderRadiuses
Shadows
Animations (duration and curve)
Guidelines (based on the gutter and the design grid we prototyped with)
Other classes...
I cant wrap my head on which of the following is more easy to maintain and refactor in the time of need..
Any suggestion on how to manage this whole Style guide -> code will be helpful,
especially on how to manage custom widget styles.
Thanks !
Basically you need to understand the concept of custom widgets and know the different types of chips that exist.
You can have several types of bullets - choice, filter, action, input - Chips. Chips - Material Design
From the different types of styles that material design offers, you adapt this to the widgets you receive from your design team. For example wrap your bullets in a Column to have an extra Label.
class MyActionChip extends StatelessWidget {
/// Default chip have a string label
const MyActionChip({
required this.label,
required this.onPressed,
Key? key,
this.onTapAvatar,
}) : child = null, super(key: key);
/// Default chip have a widget label
const MyActionChip.child({
required this.child,
required this.onPressed,
Key? key,
this.onTapAvatar,
}) : label = null, super(key: key);
final String? label;
final Widget? child;
final VoidCallback? onTapAvatar;
final Function()? onPressed;
#override
Widget build(BuildContext context) {
return InputChip(
backgroundColor: MyColors.milk,
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(MyConstants.borderRadiusMedium)),
side: BorderSide(color: MyColors.anthracite20),
),
avatar: const Icon(
MyIcons.plus,
size: 18,
color: MyColors.anthracite,
),
labelPadding:
const EdgeInsets.only(right: SdConstants.contentPaddingMedium),
label: child ??
Text(
label!,
style: const TextStyle(
color: MyColors.anthracite,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
onPressed: onPressed,
);
}
}
In complex cases, you can build a custom chip according to the complexity.
Note: the parameters that vary according to the screens and callbacks are simply exposed outside the constructor. You need several constructors/classes to customize all your chips.
I would recommend using InheritedWidget, then you don't have to rely on external packages.
For example:
class CustomStyle extends InheritedWidget {
const CustomStyle({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
final StyleData data;
static CustomStyle of(BuildContext context) {
final CustomStyle? result =
context.dependOnInheritedWidgetOfExactType<CustomStyle>();
assert(result != null, 'No CustomStyle found in context');
return result!;
}
#override
bool updateShouldNotify(CustomStyle oldWidget) => data != oldWidget.data;
}
You could create these and add to your CustomStyle in whatever way you want, maybe have multiple class depending on the widget, or separate them by colors.
class StyleData {
const StyleData({
required this.color,
required this.padding,
});
final Color color;
final EdgeInsets padding;
}
Wrap your MaterialApp with your CustomStyle, pass your data.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomStyle(
data: const StyleData(
color: Colors.blue,
padding: EdgeInsets.zero,
),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
Then you can access you styling in all your custom widgets:
class CustomContainer extends StatelessWidget {
const CustomContainer({Key? key, this.color, this.padding}) : super(key: key);
final Color? color;
final EdgeInsets? padding;
#override
Widget build(BuildContext context) {
return Container(
color: color ?? CustomStyle.of(context).data.color,
padding: padding ?? CustomStyle.of(context).data.padding,
child: const Text('Hello'),
);
}
}
Any questions just let me know.
The parent widget lists the same child widget two times. So there are two clickable SVG images. My intention is to change colour to purple when clicked and colour to grey when clicked again. It's like toggling colours (checking unchecking etc). The issue is when I click one SVG image of first one, then I can't uncheck (change colours) of the second one and vise-versa. It seems like the two widgets use the same svgColourFilled variable. But I'm not quite sure what the real issue of the code is.
Error: No errors, but colour change takes two clicks than one click to change colours if you click 1st image then try to check or uncheck the other one.
Parent Widget
import 'package:flutter/material.dart';
final String svgTap1Path = './lib/Assets/svgs/forTools/Tap1.svg';
final String svgTap2Path = './lib/Assets/svgs/forTools/Tap2.svg';
class ToolPanel extends StatefulWidget {
const ToolPanel( {Key? key}) : super(key: key);
#override
State<ToolPanel> createState() => _ToolPanelState();
}
class _ToolPanelState extends State<ToolPanel> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: [
TwoSVGs(
svgPicPath: svgTap1Path),
TwoSVGs(
svgPicPath: svgTap2Path)
])]);}}
Child Widget
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
bool svgColourFilled = false;
class TwoSVGs extends StatefulWidget {
final String? svgPicPath;
final Color fillColour ;
final Color emptyColour ;
const TwoSVGs(
{Key? key, this.svgPicPath, this.fillColour = Colors.purple, this.emptyColour= Colors.grey}) : super(key: key);
#override
State<TwoSVGs> createState() => _TwoSVGsState();
}
class _TwoSVGsState extends State<TwoSVGs> {
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Container(
child: SizedBox ( width : 80, height : 80, child:
Material(
color: Colors.transparent,
child: InkWell(
splashColor: Colors.white,
onTap: () {
setState(() {
svgColourFilled = !svgColourFilled;
}
);
},
child: FittedBox(
fit: BoxFit.contain,
child: SvgPicture.asset(
widget.svgPicPath!,
color: svgColourFilled == true
? widget.fillColour
: widget.emptyColour
)
)
))
))]
);
} }
On your code snippet, bool svgColourFilled = false;is global and used by both TwoSVGs(), put it inside state class.
class _TwoSVGsState extends State<TwoSVGs> {
bool svgColourFilled = false;
#override
Widget build(BuildContext context) {
Code A:
class MyContainer extends StatelessWidget {
const MyContainer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
// No class type
final container = Container(
width: 100,
height: 100,
color: Colors.red,
);
return container;
}
}
Code B:
class MyContainer extends StatelessWidget {
const MyContainer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
// Add class type
final Widget container = Container(
width: 100,
height: 100,
color: Colors.red,
);
return container;
}
}
I prefer Code A, but I saw some flutter source code in AppBar:
final Widget toolbar = NavigationToolbar(
leading: leading,
middle: title,
trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(theme),
middleSpacing: widget.titleSpacing,
);
It's like Code B, why flutter official add class type? For performance? For more readable? or other reasons?
Should I add class type when I use final with local widget?
Dart is type-safe and you decide to add a type or not. Type annotations are optional, because Dart VM performs runtime checks and to ensure variable’s value always matches the variable’s static type. See this for more on Dart Type System.
Lets assume a class "SpecialButton" and its State-Class "SpecialButtonState"
class SpecialButton extends StatefulWidget {
bool active = false;
SpecialButton({Key key}) : super(key: key);
#override
SpecialButtonState createState() => SpecialButtonState();
}
class SpecialButtonState extends State<SpecialButton> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
decoration:
BoxDecoration(color: this.widget.active ? COLOR_1 : COLOR_2),
child: null);
}
}
In the parent widget, I manage a couple of these buttons. Therefore, I want to assign a state to them. The solution I tried was to introduce a flag "active" in the SpecialButton class which I can easily set to either true or false from the parent widget. I can then use this in the build function of the state class to colorize the button. Unfortunately, this does not work completely as it does not update the button immediately (it needs some kind of state update e.g. by hovering over the element).
My second idea was to introduce this flag as a propper state of the SpecialButtonState class
class SpecialButton extends StatefulWidget {
SpecialButton({Key key}) : super(key: key);
#override
SpecialButtonState createState() => SpecialButtonState();
}
class SpecialButtonState extends State<SpecialButton> {
bool active;
#override
void initState() {
super.initState();
this.active = false;
}
activate() {
this.setState(() {
active = true;
});
}
deactivate() {
this.setState(() {
active = false;
});
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: this.active ? COLOR_1 : COLOR_2),
child: null);
}
}
As far as I understood, this would be the correct way to work with flutter but it seems that I can't access the functions "activate" or "deactivate" from either the SpecialButton Class or the Parent Class containing the widget.
So my question is: How can I (directly or indirectly through functions) modify a State from the corresponding StatefulWidget Class or the Parent Widget containing it?
There are already some similar questions about this on here on Stack Overflow where I could find hints both to use or not to use global keys for such behavior which i found misleading. Also, due to the rapid ongoing development of flutter, they are probably outdated so I ask this (similar) question again in relation to this exact use case.
EDIT: I forgot to mention that it is crucial that this flag will be changed after creation therefore It will be changed multiple times during its livetime. This requires the widget to redraw.
It is not neсessary to use stateful widget for SpecialButton is you case. You can handle active flag with stateless widget and keys. Example code:
class SomeParent extends StatefulWidget {
const SomeParent({Key key}) : super(key: key);
#override
State<SomeParent> createState() => SomeParentState();
}
class SomeParentState extends State<SomeParent> {
bool _button1IsActive = false;
bool _button2IsActive = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SpecialButton(
key: UniqueKey(),
active: _button1IsActive,
),
SizedBox(height: 8),
SpecialButton(
key: UniqueKey(),
active: _button2IsActive,
),
SizedBox(height: 16),
TextButton(
child: Text('Toggle button 1'),
onPressed: () {
setState(() {
_button1IsActive = !_button1IsActive;
});
},
),
SizedBox(height: 8),
TextButton(
child: Text('Toggle button 2'),
onPressed: () {
setState(() {
_button2IsActive = !_button2IsActive;
});
},
),
],
),
);
}
}
class SpecialButton extends StatelessWidget {
final bool active;
const SpecialButton({Key key, this.active = false}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: 40,
width: 40,
decoration: BoxDecoration(color: active ? Colors.red : Colors.blue),
);
}
}
SomeParent is my fantasy, just for example. Don't know what your parent is.
Keys are significant here. They tell widget tree when specific widgets with the same type (such as SpecialButton) should be rebuild.
Please try this approach, it should work.
As nvoigt says, your buttons could even be stateless widget , but their parent should be statefull and you should provide them with the corresponding value. e.g.:
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
bool isEnabled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
StateLessButton1(isEnabled: isEnabled),
StateLessButton1(isEnabled: !isEnabled),
FloatingActionButton(onPressed: (){
setState(() {
isEnabled = !isEnabled;
});
})
],
);
}
}
Now it just depends on when you want to change that value. If you want to change it inside your buttons, I would recommend you to use a class with ChangeNotifier and a function inside it that changes the value. Otherwise I would recommend not to separate your tree into multiple files