'Subclassing Widgets with complex signatures

As far as I found out, RadioListTile with Text als child has unlimited with.

As a helper class, I'd like to subclass RadioListTile with a default Text child of defined maximum width.

Unfortunately, RadioListTile has a very complex constructor.

Does a best practice exist, how to create a specialized RadioListTile and somehow abstract from the complex constructor signature?

Constructor of RadioListTile:

  const RadioListTile({
    Key? key,
    required this.value,
    required this.groupValue,
    required this.onChanged,
    this.toggleable = false,
    this.activeColor,
    this.title,
    this.subtitle,
    this.isThreeLine = false,
    this.dense,
    this.secondary,
    this.selected = false,
    this.controlAffinity = ListTileControlAffinity.platform,
    this.autofocus = false,
    this.contentPadding,
    this.shape,
    this.tileColor,
    this.selectedTileColor,
    this.visualDensity,
    this.focusNode,
    this.enableFeedback,
  }) 

Another example

Suppose I frequently use this snippet:

Text( 'Tabelle', style: TextStyle(
            fontWeight: Theme.of(context).textTheme.bodyMedium?.fontWeight,
              fontSize: Theme.of(context).textTheme.bodyMedium?.fontSize,
            ) )

I Imagine to use a specialized Text() widget like so:

TextBodyMedium( 'Tabelle' )

Ho do I orderly specialize the Text() class without enumerating all its 10+ parameters? Does a canonical way exist?



Solution 1:[1]

Update 2022-05-12: With Dart 2.17, passing arguments to superclass constructor is twice as short, see https://dart.dev/guides/language/language-tour#super-parameters Everything else below still applies.

Original answer:

In Dart, there is no way to automatically pass all the arguments to a superclass. For instance, this is how Column widget is implemented (it is a Flex widget with vertical direction):

  Column({
    Key? key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection? textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline? textBaseline,
    List<Widget> children = const <Widget>[],
  }) : super(
    children: children,
    key: key,
    direction: Axis.vertical,
    mainAxisAlignment: mainAxisAlignment,
    mainAxisSize: mainAxisSize,
    crossAxisAlignment: crossAxisAlignment,
    textDirection: textDirection,
    verticalDirection: verticalDirection,
    textBaseline: textBaseline,
  );

This is why you don't see that many packages that customize standard widgets a little bit.

If you use a customized widget it in your app and do not export it as a library, you probably do not need 80% of those arguments. So in practice we create widgets that only accept the arguments we need.

Speaking of giving a finite width to RadioListTile. There is a purpose why it is infinite and does not accept a width. In a good design, there should be only one way of setting width. One could think of automatically determining width from the container or of passing it explicitly. For many reasons in Flutter the former was chosen. Some of the benefits include automatic layout on resizing, avoidance of property drills, and less need for customized widgets. So I suggest you allow the container of your radio to determine width. That way you get rid of the whole class MyRadioListTile.

As for TextBodyMedium, I suggest you first shorten it like this:

Text('Tabelle', style: Theme.of(context).textTheme.bodyMedium);

Next, if you need to use a lot of that on one page, you can use DefaultTextStyle widget to apply the style to all widgets beneath it in the tree.

Solution 2:[2]

I think Alexey Inkin's in the right answer, but if you want a way to have some standard value for one parameter (text with a fixed width in this case) while still having access to the many other properties you could try a pattern like the code below. It's slightly more verbose in usage than simple composition (or inheritance) but you retain access to all of RadioListTile parameters.

class MyTextBuilder extends StatelessWidget {
  const MyTextBuilder({
    required this.text,
    required this.builder,
    Key? key,
  }) : super(key: key);

  final String text;
  final RadioListTile Function(BuildContext context, Widget child) builder;

  @override
  Widget build(BuildContext context) => builder(
        context,
        SizedBox(width: 100, child: Text(text)),
      );
}

// Usage:

final example = MyTextBuilder(
  text: "Hello",
  builder: (context, value) => RadioListTile(
    value: value,
    // any of the other plethora of parameters 
  ),
);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 spkersten