Version 0.21.0

Release notes and breaking changes.

TLDR / Overview

Version 0.21.0 comes with a major overhaul of the component syntax, making it easier and even more familiar to write components. It also ships a new jaspr migrate command for automatic migrations to new versions, and adds support for five new css properties.

TL;DR

  • Simplified Build Methods: All component build methods now return a single Component instead of an Iterable<Component>, making code more concise and Flutter-like.
  • Unified Component Factories: New factory constructors on the core Component class replace DomComponent(), Text(), and Fragment() for a more consistent API.
  • Automatic Migration Command: The CLI now includes a jaspr migrate command to help you automatically update your codebase for breaking changes.
  • Improved Base Handling: The base parameter of the Document() component can now be set to null to suppress the <base> element.
  • Expanded CSS Support: Eight new CSS properties are now available for more flexible styling: all, aspectRatio, appearance, justifyItems, justifySelf, alignContent, filter, and backdropFilter.

Additionally, a new version of the Jaspr VSCode extension was released that contains a major improvement to the developer experience. Read more in the Extension Release Notes.

Build Method Improvement

All component build methods now return a single Component instead of an Iterable<Component>. This includes:

  • The build() methods of StatelessComponent and StatefulComponents State:

    @override
    Iterable<Component> build(BuildContext context) sync* { 
      yield ... 
    Component build(BuildContext context) { 
      return ... 
    }
    
  • The builder parameter of Builder, ListenableBuilder, FutureBuilder, StreamBuilder and AsyncBuilder, e.g.:

    return Builder(
      builder: (context) sync* { 
        yield ... 
      builder: (context) { 
        return ... 
      }
    );
    
  • The build() method of AsyncStatelessComponent:

    Stream<Component> build(BuildContext context) async* { 
      yield ... 
    Future<Component> build(BuildContext context) async { 
      return ... 
    }
    

Migration

After upgrading Jaspr CLI to 0.21.0, you can use the jaspr migrate command to automatically migrate your components to the new build method signature.

The automatic migration will be able to migrate most of your components (90% to 99%).

In case you need or want to manually migrate single components to the new build method, migrating is straightforward and in most cases trivial:

  1. Change the method signature to Component build(BuildContext context) for normal components/builders, and Future<Component> build(BuildContext context) for async components/builders.
  2. For build methods yielding a single child, simply remove the sync* modifier and return that child intead.
  3. For build methods yielding multiple children, return a Fragment and provide the children as a List<Component>.

Reason for the Change

We noticed that in practice most components already only return/yield a single child component. For those components, changing to a single child build method is the natural choice.

Additionally, this change allows to discontinue the special sync*/yield syntax used before and instead aligns more closely with Flutter's widget syntax. And while this syntax is not officially deprecated, it is not really recommended by the Dart team as it lacks certain performance optimizations (especially in the js compiler) and doesn't support newer language features (e.g. no null-aware yield).

Lastly, changing to single-child components allowed for major simplifications and optimizations of the internal framework code of Jaspr, resulting in a less-complex, easier to maintain and more performant implementation.

Foundational Component Factories

New factory constructors on the core Component class have been introduced and replace the separate DomComponent(), Text() and Fragment() constructors:

return DomComponent(tag: 'div', ...); 
return Component.element(tag: 'div', ...); 

return Text("..."); 
return Component.text("..."); 

return Fragment(children: [...]); 
return Component.fragment([...]); 

The DomComponent, Text and Fragment classes still exist in the implementation, but cannot be created directly anymore.

Additionally, a new Component.empty() convenience constructor was added to create an empty fragment.

The html utility methods (div(), button() etc.) still exist and are still recommended over using the Component.element constructor. Same goes for the text() method and the new fragment() method for consistency, both of which are now also part of the 'prefer_html_methods' lint of jaspr_lints.

Migration

After upgrading Jaspr CLI to 0.21.0, you can use the jaspr migrate command to automatically migrate your components to the new factory API.

The automatic migration will be able to migrate 99% of your components.

In case you need or want to manually migrate single components to the new factory API, migrating is straightforward and in most cases trivial:

  1. Replace any DomComponent() invocation with Component.element().
  2. Replace any Text() invocation with Component.text().
  3. Replace any Fragment() invocation with Component.fragment() (or Component.empty() if it is an empty fragment).

Reason for the Change

This change was made to improve the API in terms of concistency and discoverability.

It is also designed to work well with the new dot shorthand syntax introduced in Dart 3.10. This allows you to omit the type name (in this case Component) when accessing a static member in a context where that type is expected (in this case e.g. a component's build method or children parameter).

It also better encapsulates the concept that Jaspr has (different to Flutter) a fixed set of foundational component types (instead of allowing to write any number of custom render objects), and thus better aligns with the internal implementation.

In real-world use we don't expect this to make much of a difference, as most Jaspr code should use the HTML utility methods anyways.

New Migrate Command

The CLI will include a new migrate command meant for helping with the migration of breaking changes in new versions. This will not only be used for the Build Method Change and Component Factories Change of this release, but also for any future change that allows for automatic migration.

Using the command is straightforward and follows the same style as the dart fix command:

  • Running jaspr migrate with no options will check only show which migrations are available for the currently used Jaspr version, e.g. Build Method Migration for version 0.21.0 and newer.
  • Running jaspr migrate --dry-run will compute and print all proposed migrations, but makes no changes to the code.
  • Running jaspr migrate --apply will compute, print and apply all migrations.
Migrate options for version 0.21.0

Base Handling

The base parameter of the Document() component can now be set to null, which causes the <base> head element to not be rendered. Previously this defaulted to / even when explicitly set to null. The default when not specifying the parameter is still /.

When setting to null, the script src path of the generated client script is now corrected and does not produce an error.

There is no migration needed, as the behavior for normal usage is still the same.

New CSS Properties

The following css properties have been added to Styles() and css.styles():

// For resetting default styles.
All all

// For box sizing and layout.
AspectRatio aspectRatio
Appearance appearance

// For flexbox and grid layouts.
JustifyItems justifyItems
JustifySelf justifySelf
AlignContent alignContent

// For applying graphical effects like blur or color shift.
Filter filter
Filter backdropFilter
  • JustifyItems, JustifySelf and AlignContent are enums with values like JustifyItems.center, JustifySelf.start or AlignItems.spaceAround.

  • Filter defines several filter methods like Filter.blur(8.px), Filter.hueRotate(90.deg), Filter.opacity(0.5) or Filter.none.