Skip to content

Instantly share code, notes, and snippets.

@iapicca
Last active November 5, 2022 23:52
Show Gist options
  • Select an option

  • Save iapicca/52e8e2ef8bad97f21dc91fa420dcec0e to your computer and use it in GitHub Desktop.

Select an option

Save iapicca/52e8e2ef8bad97f21dc91fa420dcec0e to your computer and use it in GitHub Desktop.
issue_114320
import 'dart:async';
import 'package:flutter/material.dart';
/// THIS IS TO INJECT SOMETHING THAT NEEDS TO BE INITIALIZED IN THE CONTEXT
/// [[start]]
typedef Initialize<T> = T Function();
typedef Dispose<T> = void Function(T);
class _MyInheritedWidget<T> extends InheritedWidget {
const _MyInheritedWidget({
super.key,
required this.data,
required super.child,
});
final T data;
@override
bool updateShouldNotify(_MyInheritedWidget<T> oldWidget) =>
data != oldWidget.data;
}
extension InheritedWidgetOfContextX on BuildContext {
T getInherited<T>() {
final result = dependOnInheritedWidgetOfExactType<_MyInheritedWidget<T>>();
if (result == null) {
throw Exception('No InheritedWidget<$T> found in BuildContext');
}
return result.data;
}
}
class MyInheritedWidget<T> extends StatefulWidget {
final Widget child;
final Initialize<T> initialize;
final Dispose<T> dispose;
const MyInheritedWidget({
super.key,
required this.child,
required this.initialize,
required this.dispose,
});
@override
State<MyInheritedWidget<T>> createState() => _MyInheritedWidgetState<T>();
}
class _MyInheritedWidgetState<T> extends State<MyInheritedWidget<T>> {
late final T _data;
@override
void initState() {
_data = widget.initialize();
super.initState();
}
@override
void dispose() {
widget.dispose(_data);
super.dispose();
}
@override
Widget build(context) => _MyInheritedWidget<T>(
data: _data,
child: widget.child,
);
}
/// [[end]]
void main() => runApp(const MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(context) => MyInheritedWidget<StreamController<List<String>>>(
initialize: () => StreamController.broadcast(),
dispose: (controller) => controller.close(),
child: MyTabPage(
tabs: [
TabWidgetUnion(
tab: const Tab(text: 'first'),
builder: (context) => const MyListView(),
),
TabWidgetUnion(
tab: const Tab(text: 'second'),
builder: (context) => const Center(child: Text('SECOND TAB')),
),
],
fab: const MyFab(),
),
);
}
/// THIS HANDLES THE DATA FROM THE STREAM
/// AND USES [AutomaticKeepAliveClientMixin]
class MyListView extends StatefulWidget {
const MyListView({super.key});
@override
State<MyListView> createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView>
with AutomaticKeepAliveClientMixin {
@override
final wantKeepAlive = true;
@override
Widget build(context) {
super.build(context);
return StreamBuilder<List<String>>(
stream: context.getInherited<StreamController<List<String>>>().stream,
builder: (context, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? const Center(child: CircularProgressIndicator())
: !snapshot.hasData
? const Center(child: Text('There is no data'))
: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => Text(
snapshot.data![index],
),
),
);
}
}
/// THIS IS HANDLES THE TABS ( and the fab that for whatever reason
/// is accessible also in the wrong tab
/// [[start]]
@immutable
class TabWidgetUnion {
final Tab tab;
final WidgetBuilder builder;
const TabWidgetUnion({required this.builder, required this.tab});
}
class MyTabPage extends StatefulWidget {
final List<TabWidgetUnion> tabs;
final Widget? fab;
const MyTabPage({
super.key,
this.tabs = const [],
this.fab,
});
@override
State<MyTabPage> createState() => _MyTabPageState();
}
class _MyTabPageState extends State<MyTabPage> {
@override
Widget build(context) => DefaultTabController(
length: widget.tabs.length,
child: Scaffold(
appBar: AppBar(
bottom: PreferredSize(
preferredSize: const Size.fromHeight(0),
child: SizedBox(
height: 30,
width: double.infinity,
child: TabBar(
tabs: [for (final union in widget.tabs) union.tab],
),
),
),
),
body: TabBarView(
children: [for (final union in widget.tabs) union.builder(context)],
),
floatingActionButton: widget.fab,
),
);
}
/// [[end]]
/// THIS APPENDS AND SINK A NEW STRING TO THE LIST
/// [[start]]
class MyFab extends StatelessWidget {
const MyFab({super.key});
@override
Widget build(context) {
final controller = context.getInherited<StreamController<List<String>>>();
return StreamBuilder(
stream: controller.stream,
builder: (context, snapshot) => FloatingActionButton(
onPressed: () {
final lenght = snapshot.hasData ? snapshot.data!.length : 0;
controller.sink.add([...?snapshot.data, 'data $lenght']);
},
),
);
}
}
/// [[end]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment