This cheat sheet provides a quick-reference guide to the core concepts of the Pylon library in Flutter. It includes examples of how to use immutable and mutable Pylons, future- and stream-based Pylons, as well as references to context extension methods that make working with data easy. Detailed code examples are appended at the bottom.
- WHAT IS PYLON?
- A Flutter library for sharing data throughout the widget tree.
- Operates similarly to an InheritedWidget, but provides a simpler, more flexible API.
- Offers: • Immutable data provision (Pylon) • Mutable data (MutablePylon) • Built-in helpers for Futures & Streams (PylonFuture, PylonStream) • URL syncing for web apps (PylonPort) • Navigation helpers (Pylon.push, etc.) • Global state management (Conduit)
- CORE WIDGETS
(1) Pylon • Provides a value of type T to descendant widgets. • Retrieve it in child widgets via: context.pylon() or context.pylonOr().
(2) MutablePylon • Maintains a mutable value of type T. • Descendants can update it by: context.setPylon(newValue) context.modPylon((oldValue) => newValue) • If rebuildChildren = true, descendant widgets rebuild on updates.
(3) PylonFuture • Wraps a FutureBuilder and automatically provides the completed value. • Accepts: future: Future initialData: T? builder: (context) => Widget loading/error states
(4) PylonStream • Wraps a StreamBuilder. As the stream emits values, they're exposed to descendants via Pylon.
(5) PylonPort • Designed for web usage. • Syncs a typed value with the browser’s URL (query params). • Requires registering a custom codec that implements PylonCodec. • Tip: Use registerPylonCodec(codec) somewhere in your main method to register codecs globally.
(6) PylonCluster • Combines multiple Pylon or MutablePylon widgets into one. • Syntax: PylonCluster(pylons: [Pylon(...), Pylon(...)], builder: (context) => Widget) • Use Case: Simplifies providing multiple values at once.
(7) PylonRemove • Removes a Pylon value for its subtree, making context.pylon() throw or pylonOr() return null. • Syntax: PylonRemove(builder: (context) => Widget) • Use Case: Overrides or resets a Pylon value in specific branches.
(8) Conduit A global state container for managing app-wide data or events. Unlike Pylon or MutablePylon, which rely on the widget tree, Conduit stores data in static BehaviorSubject instances. It’s helpful if you need truly global, widget-independent state. Common static methods include: • Conduit.push(value) Publishes (pushes) a new value into the global Conduit. • Conduit.pull() Fetches the most recent value from Conduit (returns an error if no Conduit has been established yet). • Conduit.mod((oldValue) => newValue) Functionally updates the value in Conduit by applying a transformation to the old value. • Conduit.stream() Returns a Stream that emits updates whenever Conduit.push or Conduit.mod changes the data. • Conduit.destroy() Destroys the Conduit instance and clears its stored data. • Conduit.destroyAllConduits() Destroys all existing Conduit streams in one call. EG: // Initialize or update user data globally: Conduit.push(User(name: 'Alice', age: 30));
// Access the current user anywhere: final user = Conduit.pull(); print(user.name); // "Alice"
// Listen for changes as a stream: final userStream = Conduit.stream(); userStream.listen((updatedUser) => print(updatedUser.name));
// Modify the user data: Conduit.mod((oldUser) => oldUser.copyWith(name: '${oldUser.name} [updated]'));
- NAVIGATION HELPERS
- Pylon.push(context, builder): Pushes a new route, preserving Pylon values.
- Pylon.pushReplacement(context, builder): Replaces the current route.
- Pylon.pushAndRemoveUntil(context, builder, predicate): Pushes a route and removes all previous routes until the predicate is met.
- Use Case: Ensures Pylon data persists during navigation.
- CONTEXT EXTENSION METHODS
Use these inside any descendant widget:
• context.pylon() => Returns the nearest Pylon value. Throws if not found.
• context.pylonOr() => Returns the nearest Pylon value or null if not found.
• context.setPylon(newValue) => Updates the value of the nearest MutablePylon.
• context.modPylon((oldVal) => newVal) => Functional update on the nearest MutablePylon.
• context.streamPylon() => Returns the update stream from a MutablePylon.
• context.watchPylon((val) => Widget) => Builds a widget that reacts to changes in a MutablePylon.
- HELPER EXTENSIONS
- stream.asPylon(): Converts a Stream into a PylonStream.
- iterable.withPylons(): Wraps an iterable with Pylon providers for each item.
- Use Case: Simplifies integrating streams or lists with Pylon.
- QUICK USAGE FLOW
Pylon<T> -> "Provide data"
MutablePylon<T> -> "Provide mutable data"
PylonFuture<T> -> "Provide data from an async future"
PylonStream<T> -> "Provide data from a stream"
PylonPort<T> -> "Sync data with URL (web-specific)"
PylonCluster -> "Combine multiple Pylons"
PylonRemove<T> -> "Remove a Pylon value for subtree"
Conduit<T> -> "Global state management"
- EXAMPLES
Below are example snippets showcasing usage of Pylon.
import ‘package:flutter/material.dart’; import ‘package:pylon/pylon.dart’;
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // Provide a User object to the widget tree return Pylon( value: User(name: ‘Alice’, age: 30), builder: (context) { return MaterialApp( home: HomePage(), ); }, ); } }
class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { // Retrieve the User from the Pylon final user = context.pylon(); return Scaffold( body: Center( child: Text('Hello, ${user.name}!'), ), ); } }
class User { final String name; final int age; User({required this.name, required this.age}); }
class CounterApp extends StatelessWidget { @override Widget build(BuildContext context) { return MutablePylon( value: 0, // initial count rebuildChildren: true, builder: (context) { return MaterialApp( home: CounterPage(), ); }, ); } }
class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { final count = context.pylon(); // read the current count return Scaffold( appBar: AppBar(title: Text('Counter: $count')), body: Center( child: ElevatedButton( onPressed: () { // increment the count context.modPylon((oldVal) => oldVal + 1); }, child: Text('Increment'), ), ), ); } }
class MyCompleteApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: PylonFuture( future: fetchUserFromApi(), loading: CircularProgressIndicator(), error: Text(‘Error fetching user’), builder: (context) { // Once future completes, we have a Pylon final user = context.pylon(); // Make it mutable for potential updates return MutablePylon( value: user, rebuildChildren: true, builder: (context) => ProfilePage(), ); }, ), ); } }
class ProfilePage extends StatelessWidget { @override Widget build(BuildContext context) { final user = context.pylon(); return Scaffold( appBar: AppBar(title: Text(user.name)), body: Center( child: ElevatedButton( onPressed: () { context.modPylon( (oldUser) => oldUser.copyWith(name: ‘${oldUser.name} [updated]’) ); }, child: Text(‘Update Name’), ), ), ); } }
class User { final String name; final int age; User({required this.name, required this.age});
User copyWith({String? name, int? age}) => User(name: name ?? this.name, age: age ?? this.age); }
Future fetchUserFromApi() async { await Future.delayed(Duration(seconds: 2)); return User(name: ‘Alice’, age: 30); }
class NavExample extends StatelessWidget { @override Widget build(BuildContext context) { return Pylon( value: 42, builder: (context) => Scaffold( body: Center( child: ElevatedButton( onPressed: () => Pylon.push(context, (context) => SecondPage()), child: Text('Go to Second Page'), ), ), ), ); } }
class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { final value = context.pylon(); return Scaffold( appBar: AppBar(title: Text('Second Page')), body: Center(child: Text('Value from Pylon: $value')), ); } }