flutter-input-output-preview

Flutter Input Output Preview

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "flutter-input-output-preview" with this command: npx skills add rodydavis/skills/rodydavis-skills-flutter-input-output-preview

Flutter Input Output Preview

First we need a two pane widget to properly render on mobile and desktop:

import 'package:flutter/material.dart';

class TwoPane extends StatefulWidget { const TwoPane({ super.key, required this.primary, required this.secondary, required this.title, this.actions = const [], this.loading = false, });

final (String, WidgetBuilder) primary, secondary; final List<Widget> actions; final String title; final bool loading;

@override State<TwoPane> createState() => _TwoPaneState(); }

class _TwoPaneState extends State<TwoPane> { bool darkMode = false;

void toggleDarkMode() { if (mounted) { setState(() { darkMode = !darkMode; }); } }

ThemeData theme(Color color, Brightness brightness) { return ThemeData( brightness: brightness, colorScheme: ColorScheme.fromSeed( seedColor: color, brightness: brightness, ), useMaterial3: true, ); }

@override void didUpdateWidget(covariant TwoPane oldWidget) { if (oldWidget.loading != widget.loading || oldWidget.title != widget.title || oldWidget.actions != widget.actions) { if (mounted) setState(() {}); } super.didUpdateWidget(oldWidget); }

@override Widget build(BuildContext context) { return Theme( data: theme(Colors.purple, darkMode ? Brightness.dark : Brightness.light), child: Builder(builder: (context) { final (primaryTitle, primaryBuilder) = widget.primary; final (secondaryTitle, secondaryBuilder) = widget.secondary; return Scaffold( appBar: AppBar( title: Text(widget.title), centerTitle: false, actions: [ IconButton( tooltip: 'Toggle dark mode', onPressed: toggleDarkMode, icon: Icon(darkMode ? Icons.light_mode : Icons.dark_mode), ), ...widget.actions, ], ), body: LayoutBuilder( builder: (context, dimens) { if (dimens.maxWidth > 800 && dimens.maxHeight > 600) { return Column( children: [ if (widget.loading) const LinearProgressIndicator(), Expanded( child: Row( children: [ Flexible( flex: 1, child: primaryBuilder(context), ), Flexible( flex: 1, child: secondaryBuilder(context), ), ], ), ), ], ); } return DefaultTabController( length: 2, child: Column( children: [ SizedBox( height: kToolbarHeight, width: double.infinity, child: TabBar( tabs: [ Tab(text: primaryTitle), Tab(text: secondaryTitle), ], ), ), if (widget.loading) const LinearProgressIndicator(), Expanded( child: TabBarView( children: [ primaryBuilder(context), secondaryBuilder(context), ], ), ), ], ), ); }, )); }), ); } }

Then we can pass some text fields for one pane to render an output:

import 'package:flutter/material.dart';

import 'two_pane.dart';

class InputOutputPreview extends StatefulWidget { const InputOutputPreview({ super.key, required this.title, required this.input, required this.output, required this.preview, required this.placeholder, this.actions = const [], this.codeTitle = 'Code', this.previewTitle = 'Preview', this.loading = false, this.lazy = false, this.previewSize = const Size(300, 700), });

final ( String, ValueChanged<(TextEditingController, TextEditingController)> ) input, output; final Widget? preview; final Widget placeholder; final String title; final List<Widget> actions; final String codeTitle, previewTitle; final Size? previewSize; final bool loading; final bool lazy;

@override State<InputOutputPreview> createState() => _InputOutputPreviewState(); }

class _InputOutputPreviewState extends State<InputOutputPreview> { final input = TextEditingController(); final output = TextEditingController(); String? lastInput; String? lastOutput;

@override void initState() { super.initState(); if (!widget.lazy) input.addListener(onInput); output.addListener(onOutput); }

@override void dispose() { super.dispose(); if (!widget.lazy) input.removeListener(onInput); output.removeListener(onOutput); input.dispose(); output.dispose(); }

void onInput() { final (_, update) = widget.input; final str = input.text; if (lastInput == str) return; update((input, output)); lastInput = str; }

void onOutput() { final (_, update) = widget.output; final str = output.text; if (lastOutput == str) return; update((output, input)); lastOutput = str; }

@override Widget build(BuildContext context) { final (inputTitle, _) = widget.input; final (outputTitle, _) = widget.output; return TwoPane( title: widget.title, actions: widget.actions, loading: widget.loading, primary: ( widget.codeTitle, (context) => SizedBox( height: double.infinity, child: Column( children: [ Flexible( child: Padding( padding: const EdgeInsets.all(8), child: Card( child: ListTile( title: Text(inputTitle), subtitle: TextField( maxLines: null, controller: input, expands: true, decoration: InputDecoration( isCollapsed: true, border: InputBorder.none, suffix: widget.lazy ? IconButton( onPressed: onInput, icon: const Icon(Icons.save), tooltip: 'Submit', ) : null, ), ), ), ), ), ), Flexible( child: Padding( padding: const EdgeInsets.all(8), child: Card( child: ListTile( title: Text(outputTitle), subtitle: TextField( maxLines: null, controller: output, expands: true, decoration: const InputDecoration( isCollapsed: true, border: InputBorder.none, ), ), ), ), ), ), ], ), ), ), secondary: ( widget.previewTitle, (context) => Container( color: Theme.of(context).colorScheme.surfaceVariant, child: Builder(builder: (context) { if (widget.previewSize == null) { return widget.preview ?? widget.placeholder; } return Center( child: Material( elevation: 8, child: SizedBox.fromSize( size: widget.previewSize, child: widget.preview ?? widget.placeholder, ), ), ); }), ) ), ); } }

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

flutter-control-and-screenshot

No summary provided by upstream source.

Repository SourceNeeds Review
General

install-flutter-from-git

No summary provided by upstream source.

Repository SourceNeeds Review
General

how-to-build-a-native-cross-platform-project-with-flutter

No summary provided by upstream source.

Repository SourceNeeds Review
General

how-to-build-a-webrtc-signal-server-with-pocketbase

No summary provided by upstream source.

Repository SourceNeeds Review