Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): Add documentation for using records and typedef #6382

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Asncodes-80
Copy link

@Asncodes-80 Asncodes-80 commented Feb 2, 2025

This commit introduces a new section in the documentation showcasing the combination of Dart's record types and typedef to create a simple, reusable data structure for dynamic UI rendering.

This approach was originally shared as an answer on Stack Overflow in Spring 2023 to the question: "Is there anything like a struct in Dart?" (https://stackoverflow.com/a/76391196/12158825). The solution received highly positive feedback via emails and comments, highlighting its usefulness for developers.

This feature is now documented as a recommended approach for developers aiming to write cleaner and more maintainable Dart code that wasn't mentioned before.

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
  • This PR doesn't contain automatically generated corrections or text (Grammarly, LLMs, and similar).
  • This PR follows the Google Developer Documentation Style Guidelines — for example, it doesn't use i.e. or e.g., and it avoids I and we (first person).
  • This PR uses semantic line breaks of 80 characters or fewer.
    Waiting for the further feedback.

Thank you!

This commit introduces a new section in the documentation showcasing the
combination of Dart's record types and `typedef` to create simple,
reusable data structure for dynamic UI rendering.

This approach was originally shared as an answer on Stack Overflow in
Spring 2023 to the question: "Is there anything like a struct in Dart?"
(https://stackoverflow.com/a/76391196/12158825). The solution received
highly positive feedback via emails and comments, highlighting its
usefulness for developers.

This feature is now documented as a recommended approach for developers
aiming to write cleaner and more maintainable Dart code that wasn't
mentioned before.
Copy link

google-cla bot commented Feb 2, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@MaryaBelanger
Copy link
Contributor

/gcbrun

@dart-github-bot
Copy link
Collaborator

Visit the preview URL for this PR (updated for commit 1436da5):

https://dart-dev--pr6382-records-interface-36t52ip3.web.app

@MaryaBelanger
Copy link
Contributor

Hi @lrhn, could you give your thoughts on this addition, since you left the accepted answer on the stack overflow question? Thank you!

@lrhn
Copy link
Member

lrhn commented Feb 5, 2025

I think the text is a little too unfocused. It's not clear what it's trying to tell the user, and unclear on why this code would be better or worse than alternatives. It's just an example, and it lacks a motivation for that example, so the reader is likely to not know what their take-away should be.

The text can be improved, but that's just editing. We can fix that.
I want it to have a clear focus and motivation for the section, and I'm not sure what the intended focus and motivation is for the current text, making me feel like asking "Why are you telling me this?".

(The text starts out by referring to the previous section as just "The provided example", which makes a reader think it should be something closer. It mentions UI code in a way that feels out of the blue, as something that the example shows something about, even though this is the first mention of UI in the document, and it's not mentioned in any other section. The reference to JavaScript interfaces may be relevant, but should have a link to a definition or explanation of what it is, a reader cannot be assumed to know already. Claims are made, but are generally unsupported: "without the overhead of classes" (which overhead? syntactic or runtime?), "particularly useful for..." (how?).
As a section, the text should be self-contained, or at least make references to other sections explicit and clear, so the reader knows where to go and look if they haven't read it recently. It should also fit into the document, so if there are any running examples it can use, that would be better than making up ones from scratch, from a domain that isn't otherwise mentioned.)

If the section was presented as just an example, of how you can use records as simple data objects,
without making any claims about whether they are better or worse than any other choice,
then I think it would work.

Something like:

Records as simple data structures

Records only hold data, but if that is all you need, they're immediately available without needing
to declare any new classes.
For a simple list of data tuples which all have the same shape, a list of records is the most direct
representation.

Take this list of "button definitions":

final buttons = [
  (
    label: "Button I",
    icon: const Icon(Icons.upload_file),
    onPressed: () => print("Action -> Button I"),
  ),
  (
    label: "Button II",
    icon: const Icon(Icons.info),
    onPressed: () => print("Action -> Button II"),
  )
];

This code can be written directly without needing any further declarations.

One can choose to give the record type itself a name, and use that rather
than writing out the full record type. That also allows stating that some
fields can be null, even if none of the current entries in the list
have a null value.

typedef ButtonItem = ({String label, Icon icon, void Function()? onPressed});
final List<ButtonItem> buttons = [
  // ...
];

Because record types are structural types, that does not introduce any
new type names, just another way to refer to the record type.
However if all code refers to the record type by its alias, it's more likely
that you can later change the implementation.

Code can work with such button definitions the same it would
with simple class instances.

  List<Container> widget = [
    for (var button in buttons)
      Container(
        margin: const EdgeInsets.all(4.0),
        child: OutlinedButton.icon(
          onPressed: button.onPressed,
          icon: button.icon,
          label: Text(button.label),
        ),
      ),
  ];

It may be possible to later change the type to a class type to add methods.

class ButtonItem {
  final String label;
  final Icon icon;
  final void Function()? onPressed;
  ButtonItem({required this.label, required this.icon, this.onPressed});
  bool get hasOnpressed => onPressed != null;
}

or to an extension type:

extension type ButtonItem._(({String label, Icon icon, void Function()? onPressed}) _) {
  String get label => _.label;
  Icon get icon => _.icon;
  void Function()? get onPressed => _.onPressed;
  ButtonItem({required String label, required Icon icon, void Function()? onPressed})
      : this._((label: label, icon: icon, onPressed: onPressed));
  bool get hasOnpressed => _.onPressed != null;
}

and then create the list of button definitions using that type's constructors:

final List<ButtonItem> buttons =  [
  ButtonItem(
    label: "Button I",
    icon: const Icon(Icons.upload_file),
    onPressed: () => print("Action -> Button I"),
  ),
  ButtonItem(
    label: "Button II",
    icon: const Icon(Icons.info),
    onPressed: () => print("Action -> Button II"),
  )
];

while not needing to change the code that uses that list.

Changing any type does require the code that uses it to be very careful about
not making assumptions. A type alias does not offer any protection from code
using that a value is a record. An extension type offers a little protections,
but only a class provides full abstraction and control over access to its state.

Or something :)

I might be over-embellishing, and making this too long for a simple example.
(And since I know what I want to say, it's hard to say if I have actually said it in
a reasonable order.)

@MaryaBelanger
Copy link
Contributor

I might be over-embellishing, and making this too long for a simple example.

Not at all @lrhn, this is great! I was expecting just a review of the code alone, but I feel like you gave me a world-class lesson in technical writing in the process! I really appreciate your consideration of the docs :)

@Asncodes-80, I'll update the PR to incorporate Lasse's guidance and then we can get this published on the site. Thanks so much for bringing this to our attention!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants