Last active
November 5, 2022 23:52
-
-
Save iapicca/52e8e2ef8bad97f21dc91fa420dcec0e to your computer and use it in GitHub Desktop.
issue_114320
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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