proviso

stability-stable License

A complete set of tools for conditional rendering (if-else and switch conditions), subtree wrapping with a parent widget, async state handling (Future/Stream), platform-aware rendering, reactive ValueNotifier conditions, animated transitions, and some handy shortcuts (like DebugWidget, WebOnlyWidget, SafeBuilder, and many more). :+1:

Install

In flutter project add the dependency:

dependencies:
  ...
  proviso: ^2.0.0

Why

To make a more readable and simpler conditional statement code.

Widgets

ConditionWidget

Render a widget or fallback based on a condition:

Row(
  children: [
    ConditionWidget(
      condition: starred,
      widget: Icon(Icons.favorite),
      fallback: fallbackWidget,
    ),
    ConditionWidget(
      condition: archived,
      widget: Icon(Icons.archive),
    ),
  ],
)

ConditionBuilder

Lazy builder variant — widgets are only built when needed:

ConditionBuilder(
  condition: (_) => someCondition,
  trueBuilder: (_) => trueWidget,
  fallbackBuilder: (_) => fallbackWidget,
);

Conditional & ConditionalBuilder (static methods)

Useful inside children lists without wrapping in a widget:

Column(
  children: [
    Conditional.widget(
      context: context,
      condition: isActive,
      widget: Text('Active'),
      fallback: Text('Inactive'),
    ),
    ...Conditional.widgets(
      context: context,
      condition: hasItems,
      widgets: [Item1(), Item2()],
    ),
  ],
)

Generic builder:

final String label = ConditionalBuilder.generic<String>(
  context: context,
  condition: (_) => isSelected,
  trueBuilder: (_) => 'Selected',
  fallbackBuilder: (_) => 'Not selected',
);

SwitchCase & SwitchCaseBuilder

Switch-case rendering with maps:

SwitchCaseBuilder.widget<String>(
  context: context,
  condition: (_) => status,
  caseBuilders: {
    'active': (_) => ActiveWidget(),
    'inactive': (_) => InactiveWidget(),
  },
  fallbackBuilder: (_) => UnknownWidget(),
);

Widget variant:

SwitchCase.widget<ThemeMode>(
  context: context,
  condition: ThemeMode.dark,
  caseWidgets: {
    ThemeMode.dark: DarkIcon(),
    ThemeMode.light: LightIcon(),
  },
);

ConditionalWrap

Conditionally wrap a child in a parent widget:

ConditionalWrap(
  shouldWrap: needsPadding,
  child: MyWidget(),
  parentBuilder: (child) => Padding(
    padding: EdgeInsets.all(16),
    child: child,
  ),
)

With an else branch:

ConditionalWrap(
  shouldWrap: isSelected,
  child: Text('Hello'),
  parentBuilder: (child) => Container(
    color: Colors.blue,
    child: child,
  ),
  elseParentBuilder: (child) => Container(
    color: Colors.grey,
    child: child,
  ),
)

SafeBuilder

Try/catch for widget building:

SafeBuilder(
  widgetBuilder: (_) => riskyWidget,
  fallbackBuilder: (error, stackTrace, _) => ErrorWidget(error),
)

PlatformWidget

Render different widgets per platform:

PlatformWidget(
  android: (_) => MaterialWidget(),
  ios: (_) => CupertinoWidget(),
  web: (_) => WebWidget(),
  fallback: (_) => DefaultWidget(),
)

MediaQueryCondition

Conditional rendering based on screen size:

MediaQueryCondition(
  condition: (_, mq) => mq.size.width > 600,
  builder: (_) => WideLayout(),
  fallbackBuilder: (_) => NarrowLayout(),
)

OrientationCondition

Landscape vs portrait:

OrientationCondition(
  landscape: (_) => LandscapeLayout(),
  portrait: (_) => PortraitLayout(),
)

AsyncConditional

Handle Future states (loading/data/error):

AsyncConditional<User>(
  future: fetchUser(),
  loading: (_) => CircularProgressIndicator(),
  data: (_, user) => Text(user.name),
  error: (_, error, __) => Text('Error: $error'),
)

StreamConditional

Handle Stream states:

StreamConditional<int>(
  stream: counterStream,
  data: (_, count) => Text('$count'),
  loading: (_) => Text('Waiting...'),
)

AnimatedConditional

Animated transition between conditions:

AnimatedConditional(
  condition: isVisible,
  trueWidget: DetailView(),
  falseWidget: SummaryView(),
  duration: Duration(milliseconds: 300),
)

With custom transition:

AnimatedConditional(
  condition: isVisible,
  trueWidget: DetailView(),
  falseWidget: SummaryView(),
  transitionBuilder: (child, animation) => ScaleTransition(
    scale: animation,
    child: child,
  ),
)

ReactiveConditional

Rebuilds when a ValueNotifier changes:

final isActive = ValueNotifier<bool>(false);

ReactiveConditional<bool>(
  notifier: isActive,
  condition: (value) => value,
  trueBuilder: (_) => ActiveWidget(),
  fallbackBuilder: (_) => InactiveWidget(),
)

IfNotEmpty

Conditional rendering based on collection emptiness:

IfNotEmpty<User>(
  items: users,
  builder: (_, users) => UserList(users: users),
  fallbackBuilder: (_) => EmptyState(),
)

Map variant:

IfNotEmptyMap<String, Widget>(
  items: sections,
  builder: (_, sections) => SectionList(sections: sections),
)

Shortcuts

DebugModeWidget(widget: Text('Only visible in debug'))
ReleaseModeWidget(widget: Text('Only visible in release'))
WebOnlyWidget(widget: Text('Only visible on web'))

Contributions

Feel free to report bugs, request new features or to contribute to this project!

Libraries

proviso