Skip to content

Instantly share code, notes, and snippets.

@NextdoorPsycho
Created March 28, 2025 08:15
Show Gist options
  • Save NextdoorPsycho/033a22d05bab60fbb31dfd51d5c034b4 to your computer and use it in GitHub Desktop.
Save NextdoorPsycho/033a22d05bab60fbb31dfd51d5c034b4 to your computer and use it in GitHub Desktop.
Pylon CheatSheet

======================================================== PYLON CHEAT SHEET

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.


  1. 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)

  1. 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]'));


  1. 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.

  1. 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.


  1. 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.

  1. 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"

  1. EXAMPLES

Below are example snippets showcasing usage of Pylon.


EXAMPLE 1: Immutable 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}); }


EXAMPLE 2: MutablePylon with Increment

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'), ), ), ); } }


EXAMPLE 3: Combining PylonFuture and MutablePylon

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); }


EXAMPLE 4: Navigation with Pylon

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')), ); } }


END OF CHEAT SHEET

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment