1- import 'package:flet_view/controls/error.dart' ;
21import 'package:flet_view/utils/icons.dart' ;
32import 'package:flutter/material.dart' ;
43import 'package:flutter_redux/flutter_redux.dart' ;
54
5+ import '../actions.dart' ;
66import '../models/app_state.dart' ;
77import '../models/control.dart' ;
8- import '../models/control_view_model.dart' ;
8+ import '../models/controls_view_model.dart' ;
9+ import '../protocol/update_control_props_payload.dart' ;
910import '../web_socket_client.dart' ;
1011import 'create_control.dart' ;
1112
@@ -32,12 +33,19 @@ class _TabsControlState extends State<TabsControl>
3233 List <String > _tabsIndex = [];
3334 String ? _value;
3435 TabController ? _tabController;
36+ dynamic _dispatch;
3537
3638 @override
3739 void initState () {
3840 super .initState ();
39- _tabsIndex = widget.children.map ((c) => c.attrString ("key" , "" )! ).toList ();
40- _tabController = TabController (length: _tabsIndex.length, vsync: this );
41+ _tabsIndex = widget.children
42+ .map ((c) => c.attrString ("key" ) ?? c.attrString ("text" , "" )! )
43+ .toList ();
44+ _tabController = TabController (
45+ length: _tabsIndex.length,
46+ animationDuration: Duration (
47+ milliseconds: widget.control.attrInt ("animationDuration" , 50 )! ),
48+ vsync: this );
4149 _tabController! .addListener (_tabChanged);
4250 }
4351
@@ -48,6 +56,12 @@ class _TabsControlState extends State<TabsControl>
4856 var value = _tabsIndex[_tabController! .index];
4957 if (_value != value) {
5058 debugPrint ("Selected tab: $value " );
59+ List <Map <String , String >> props = [
60+ {"i" : widget.control.id, "value" : value}
61+ ];
62+ _dispatch (
63+ UpdateControlPropsAction (UpdateControlPropsPayload (props: props)));
64+ ws.updateControlProps (props: props);
5165 ws.pageEventFromWeb (
5266 eventTarget: widget.control.id,
5367 eventName: "change" ,
@@ -60,13 +74,18 @@ class _TabsControlState extends State<TabsControl>
6074 Widget build (BuildContext context) {
6175 debugPrint ("TabsControl build: ${widget .control .id }" );
6276
63- var tabsIndex =
64- widget.children.map ((c) => c.attrString ("key" , "" )! ).toList ();
77+ var tabsIndex = widget.children
78+ .map ((c) => c.attrString ("key" ) ?? c.attrString ("text" , "" )! )
79+ .toList ();
6580 if (tabsIndex.length != _tabsIndex.length ||
6681 ! tabsIndex.every ((item) => _tabsIndex.contains (item))) {
6782 _tabsIndex =
6883 widget.children.map ((c) => c.attrString ("key" , "" )! ).toList ();
69- _tabController = TabController (length: _tabsIndex.length, vsync: this );
84+ _tabController = TabController (
85+ length: _tabsIndex.length,
86+ animationDuration: Duration (
87+ milliseconds: widget.control.attrInt ("animationDuration" , 50 )! ),
88+ vsync: this );
7089 _tabController! .addListener (_tabChanged);
7190 }
7291
@@ -82,72 +101,75 @@ class _TabsControlState extends State<TabsControl>
82101 }
83102 }
84103
85- var tabs = Column (
86- crossAxisAlignment: CrossAxisAlignment .start,
87- children: [
88- TabBar (
89- controller: _tabController,
90- isScrollable: true ,
91- indicatorColor: Theme .of (context).colorScheme.primary,
92- labelColor: Theme .of (context).colorScheme.primary,
93- unselectedLabelColor: Theme .of (context).colorScheme.onSurface,
94- tabs: widget.children
95- .map ((c) => StoreConnector <AppState , ControlViewModel >(
96- distinct: true ,
97- converter: (store) {
98- return ControlViewModel .fromStore (store, c.id);
99- },
100- builder: (context, tabView) {
101- var text = tabView.control.attrString ("text" );
102- var icon = getMaterialIcon (
103- tabView.control.attrString ("icon" , "" )! );
104- var tabContentCtrls = tabView.children
105- .where ((c) => c.name == "tab_content" );
106-
107- Widget tabChild;
108- List <Widget > widgets = [];
109- if (tabContentCtrls.isNotEmpty) {
110- tabChild = createControl (
111- widget.control, tabContentCtrls.first.id, disabled);
112- } else {
113- if (icon != null ) {
114- widgets.add (Icon (icon));
115- if (text != null ) {
116- widgets.add (const SizedBox (width: 8 ));
117- }
118- }
119- if (text != null ) {
120- widgets.add (Text (text));
104+ var tabs = StoreConnector <AppState , ControlsViewModel >(
105+ distinct: true ,
106+ converter: (store) => ControlsViewModel .fromStore (
107+ store, widget.children.map ((c) => c.id)),
108+ builder: (content, viewModel) {
109+ _dispatch = viewModel.dispatch;
110+
111+ // check if all tabs have no content
112+ bool emptyTabs = ! viewModel.controlViews
113+ .any ((t) => t.children.any ((c) => c.name == "content" ));
114+
115+ var tabBar = TabBar (
116+ controller: _tabController,
117+ isScrollable: true ,
118+ indicatorColor: Theme .of (context).colorScheme.primary,
119+ labelColor: Theme .of (context).colorScheme.primary,
120+ unselectedLabelColor: Theme .of (context).colorScheme.onSurface,
121+ tabs: viewModel.controlViews.map ((tabView) {
122+ var text = tabView.control.attrString ("text" );
123+ var icon =
124+ getMaterialIcon (tabView.control.attrString ("icon" , "" )! );
125+ var tabContentCtrls =
126+ tabView.children.where ((c) => c.name == "tab_content" );
127+
128+ Widget tabChild;
129+ List <Widget > widgets = [];
130+ if (tabContentCtrls.isNotEmpty) {
131+ tabChild = createControl (
132+ widget.control, tabContentCtrls.first.id, disabled);
133+ } else {
134+ if (icon != null ) {
135+ widgets.add (Icon (icon));
136+ if (text != null ) {
137+ widgets.add (const SizedBox (width: 8 ));
138+ }
139+ }
140+ if (text != null ) {
141+ widgets.add (Text (text));
142+ }
143+ tabChild = Row (
144+ children: widgets,
145+ mainAxisAlignment: MainAxisAlignment .center);
146+ }
147+ return Tab (child: tabChild);
148+ }).toList ());
149+
150+ if (emptyTabs) {
151+ return tabBar;
152+ }
153+
154+ return Column (
155+ crossAxisAlignment: CrossAxisAlignment .start,
156+ children: [
157+ tabBar,
158+ Expanded (
159+ child: TabBarView (
160+ controller: _tabController,
161+ children: viewModel.controlViews.map ((tabView) {
162+ var contentCtrls =
163+ tabView.children.where ((c) => c.name == "content" );
164+ if (contentCtrls.isEmpty) {
165+ return const SizedBox .shrink ();
121166 }
122- tabChild = Row (
123- children: widgets,
124- mainAxisAlignment: MainAxisAlignment .center);
125- }
126- return Tab (child: tabChild);
127- }))
128- .toList ()),
129- Expanded (
130- child: TabBarView (
131- controller: _tabController,
132- children: widget.children
133- .map ((c) => StoreConnector <AppState , ControlViewModel >(
134- distinct: true ,
135- converter: (store) {
136- return ControlViewModel .fromStore (store, c.id);
137- },
138- builder: (context, tabView) {
139- var contentCtrls = tabView.children
140- .where ((c) => c.name == "content" );
141- if (contentCtrls.isEmpty) {
142- return const ErrorControl (
143- "Tab should have a content." );
144- }
145- return createControl (
146- widget.control, contentCtrls.first.id, disabled);
147- }))
148- .toList ()))
149- ],
150- );
167+ return createControl (
168+ widget.control, contentCtrls.first.id, disabled);
169+ }).toList ()))
170+ ],
171+ );
172+ });
151173
152174 return constrainedControl (tabs, widget.parent, widget.control);
153175 }
0 commit comments