diff --git a/lib/Providers/args_provider.dart b/lib/Providers/args_provider.dart new file mode 100644 index 0000000..e3b84e5 --- /dev/null +++ b/lib/Providers/args_provider.dart @@ -0,0 +1,23 @@ +import "package:flutter/material.dart"; + +class ArgsProvider extends ChangeNotifier { + List controllers = [TextEditingController()]; + + void addController() { + controllers.add(TextEditingController()); + notifyListeners(); + } + + void removeController(int index) { + controllers.removeAt(index); + notifyListeners(); + } + + @override + void dispose() { + for (final controller in controllers) { + controller.dispose(); + } + super.dispose(); + } +} diff --git a/lib/Providers/kwargs_provider.dart b/lib/Providers/kwargs_provider.dart new file mode 100644 index 0000000..bf2683c --- /dev/null +++ b/lib/Providers/kwargs_provider.dart @@ -0,0 +1,19 @@ +import "package:flutter/cupertino.dart"; + +class KwargsProvider extends ChangeNotifier { + final List> _tableData = [ + {"key": "", "value": ""}, + ]; + + List> get tableData => _tableData; + + void addRow(Map rowData) { + _tableData.add(rowData); + notifyListeners(); + } + + void removeRow(int index) { + _tableData.removeAt(index); + notifyListeners(); + } +} diff --git a/lib/constants.dart b/lib/constants.dart new file mode 100644 index 0000000..15fe118 --- /dev/null +++ b/lib/constants.dart @@ -0,0 +1,15 @@ +import "package:flutter/material.dart"; + +/// Colors /// +Color homeAppBarBackgroundColor = Colors.white; +Color homeAppBarTextColor = Colors.black; +Color blackColor = Colors.black; +Color dropDownTextColor = Colors.green; +Color blueAccentColor = Colors.blueAccent; +Color whiteColor = Colors.white; +Color closeIconColor = Colors.redAccent; + +/// Icon Size /// +const double iconSize = 15; +const double largeIconSize = 20; +const double horizontalPadding = 10; diff --git a/lib/main.dart b/lib/main.dart index e2632f6..65f42e6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,9 @@ import "package:flutter/material.dart"; +import "package:provider/provider.dart"; +import "package:xconn_ui/Providers/args_provider.dart"; +import "package:xconn_ui/Providers/kwargs_provider.dart"; +import "package:xconn_ui/responsive/responsive_layout.dart"; +import "package:xconn_ui/screens/mobile/mobile_home.dart"; void main() { runApp(const MyApp()); @@ -7,119 +12,26 @@ void main() { class MyApp extends StatelessWidget { const MyApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: "Flutter Demo", - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: "Flutter Demo Home Page"), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({required String title, super.key}) : _title = title; - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String _title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget._title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - "You have pushed the button this many times:", - ), - Text( - "$_counter", - style: Theme.of(context).textTheme.headlineMedium, - ), - ], + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => ArgsProvider()), + ChangeNotifierProvider(create: (context) => KwargsProvider()), + ], + child: MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + fontFamily: "Arial", + useMaterial3: true, + ), + home: const ResponsiveLayout( + mobileScaffold: MobileHomeScaffold(), + tabletScaffold: MobileHomeScaffold(), + desktopScaffold: MobileHomeScaffold(), ), ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: "Increment", - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. ); } } diff --git a/lib/responsive/responsive_layout.dart b/lib/responsive/responsive_layout.dart new file mode 100644 index 0000000..bf82a4c --- /dev/null +++ b/lib/responsive/responsive_layout.dart @@ -0,0 +1,28 @@ +import "package:flutter/material.dart"; + +class ResponsiveLayout extends StatelessWidget { + const ResponsiveLayout({ + required this.mobileScaffold, + required this.tabletScaffold, + required this.desktopScaffold, + super.key, + }); + final Widget mobileScaffold; + final Widget tabletScaffold; + final Widget desktopScaffold; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth < 500) { + return mobileScaffold; + } else if (constraints.maxWidth < 1100) { + return tabletScaffold; + } else { + return desktopScaffold; + } + }, + ); + } +} diff --git a/lib/screens/mobile/mobile_home.dart b/lib/screens/mobile/mobile_home.dart new file mode 100644 index 0000000..690a0eb --- /dev/null +++ b/lib/screens/mobile/mobile_home.dart @@ -0,0 +1,718 @@ +import "package:flutter/material.dart"; +import "package:provider/provider.dart"; +import "package:xconn_ui/Providers/args_provider.dart"; +import "package:xconn_ui/Providers/kwargs_provider.dart"; +import "package:xconn_ui/constants.dart"; +import "package:xconn_ui/utils/args_screen.dart"; +import "package:xconn_ui/utils/kwargs_screen.dart"; +import "package:xconn_ui/utils/tab_data_class.dart"; + +class MobileHomeScaffold extends StatefulWidget { + const MobileHomeScaffold({super.key}); + + @override + State createState() => _MobileHomeScaffoldState(); +} + +class _MobileHomeScaffoldState extends State with TickerProviderStateMixin { + late TabController _tabController; + final List _tabNames = ["Tab 1"]; + final List _tabContents = ["Content for Tab 1"]; + final List _tabData = [TabData()]; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: _tabNames.length, vsync: this); + _tabController.addListener(_handleTabSelection); + } + + // HANDLE TABS SELECTION + void _handleTabSelection() { + setState(() {}); + } + + void _addTab() { + setState(() { + int newIndex = _tabNames.length + 1; + _tabNames.add("Tab $newIndex"); + _tabContents.add("Content for Tab $newIndex"); + _tabData.add(TabData()); + if (_tabController.length != _tabNames.length) { + _tabController = TabController(length: _tabNames.length, vsync: this); + _tabController.addListener(_handleTabSelection); + } + }); + } + + void _removeTab(int index) { + setState(() { + _tabNames.removeAt(index); + _tabContents.removeAt(index); + _tabData[index].disposeControllers(); + _tabData.removeAt(index); + + if (_tabController.length != _tabNames.length) { + _tabController = TabController(length: _tabNames.length, vsync: this); + _tabController.addListener(_handleTabSelection); + } + }); + } + + @override + void dispose() { + _tabController + ..removeListener(_handleTabSelection) + ..dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + "XConn", + style: TextStyle(color: homeAppBarTextColor, fontSize: 15), + ), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 15), + child: IconButton( + onPressed: _addTab, + icon: const Icon(Icons.add_box_sharp), + ), + ), + ], + bottom: _tabNames.isNotEmpty + ? PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: TabBar( + controller: _tabController, + isScrollable: true, + indicatorColor: blueAccentColor, + indicatorWeight: 1, + tabs: _tabNames + .asMap() + .entries + .map( + (entry) => _buildTabWithDeleteButton(entry.key, entry.value), + ) + .toList(), + ), + ) + : null, + ), + drawer: const Drawer(), + body: _tabNames.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(top: 10), + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), + controller: _tabController, + children: _tabContents.asMap().entries.map((entry) => _buildTab(entry.key)).toList(), + ), + ) + : const Center(child: Text("No Tabs")), + ); + } + + // Delete Tab + Widget _buildTabWithDeleteButton(int index, String tabName) { + final isSelected = _tabController.index == index; + return GestureDetector( + onTap: () { + _tabController.animateTo(index); + }, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + tabName, + style: TextStyle( + color: isSelected ? blueAccentColor : blackColor, + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + ), + ), + IconButton( + icon: Icon( + Icons.close, + color: closeIconColor, + size: iconSize, + ), + onPressed: () => _removeTab(index), + ), + ], + ), + ), + ); + } + + // Main Build Tab + Widget _buildTab(int index) { + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey), + ), + child: Row( + children: [ + SizedBox( + width: 100, + child: DropdownButton( + value: _tabData[index].selectedValue.isEmpty ? null : _tabData[index].selectedValue, + hint: Text( + "Actions", + style: TextStyle(color: dropDownTextColor), + ), + items: [ + "Register", + "Subscribe", + "Call", + "Publish", + ].map((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: TextStyle(color: dropDownTextColor), + ), + ); + }).toList(), + onChanged: (String? newValue) { + setState(() { + _tabData[index].selectedValue = newValue!; + switch (newValue) { + case "Subscribe": + { + _tabData[index].sendButtonText = "Subscribe"; + break; + } + case "Register": + { + _tabData[index].sendButtonText = "Register"; + break; + } + case "Call": + { + _tabData[index].sendButtonText = "Call"; + break; + } + case "Publish": + { + _tabData[index].sendButtonText = "Publish"; + break; + } + default: + { + _tabData[index].sendButtonText = "Send"; + } + } + }); + }, + ), + ), + Container( + height: 30, + width: 1, + color: Colors.grey, + ), + Expanded( + child: TextFormField( + controller: _tabData[index].linkController, + decoration: const InputDecoration( + hintText: "Enter URL or paste text", + labelText: "Enter URL or paste text", + border: InputBorder.none, + contentPadding: EdgeInsets.all(10), + ), + ), + ), + ], + ), + ), + ), + const SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + ), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + child: DropdownButton( + value: _tabData[index].selectedSerializer.isEmpty ? null : _tabData[index].selectedSerializer, + hint: const Text("Serializers"), + items: [ + "JSON", + "CBOR", + "MsgPack", + ].map((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + onChanged: (String? newValue) { + setState(() { + _tabData[index].selectedSerializer = newValue!; + }); + }, + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: TextFormField( + controller: _tabData[index].realmController, + decoration: InputDecoration( + hintText: "Enter realm here", + labelText: "Enter realm here", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(10), + ), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + + // Topic Procedure TextFormFields + buildTopicProcedure(_tabData[index].topicProcedureController, _tabData[index].sendButtonText), + + const SizedBox( + height: 20, + ), + + // Args + buildArgs(_tabData[index].sendButtonText), + + const SizedBox( + height: 20, + ), + + // K-Wargs + buildKwargs(_tabData[index].sendButtonText), + + const SizedBox(height: 20), + + // Send Button + sendButton(_tabData[index].sendButtonText), + + const SizedBox( + height: 50, + ), + Padding( + padding: const EdgeInsets.only(left: 25), + child: Align( + alignment: Alignment.topLeft, + child: SizedBox( + height: 100, + width: MediaQuery.of(context).size.width, + child: Text( + "Result", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: blackColor, + ), + ), + ), + ), + ), + const SizedBox( + height: 40, + ), + ], + ), + ); + } + + // Send Button Widget + Widget sendButton(String sendButton) { + switch (sendButton) { + case "Publish": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 110), + child: MaterialButton( + onPressed: () {}, + color: Colors.blueAccent, + minWidth: 200, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Text( + sendButton, + style: TextStyle( + color: whiteColor, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + + case "Subscribe": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 110), + child: MaterialButton( + onPressed: () {}, + color: Colors.blueAccent, + minWidth: 200, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Text( + sendButton, + style: TextStyle( + color: whiteColor, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + + case "Call": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 110), + child: MaterialButton( + onPressed: () {}, + color: Colors.blueAccent, + minWidth: 200, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Text( + sendButton, + style: TextStyle( + color: whiteColor, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + + case "Register": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 110), + child: MaterialButton( + onPressed: () {}, + color: Colors.blueAccent, + minWidth: 200, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Text( + sendButton, + style: TextStyle( + color: whiteColor, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + + default: + return Container(); + } + } + + // Topic and Procedure TextFormFields Widget + Widget buildTopicProcedure(TextEditingController controller, String sendButtonText) { + switch (sendButtonText) { + case "Publish": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: TextFormField( + controller: controller, + decoration: InputDecoration( + hintText: "Enter topic here", + labelText: "Enter topic here", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(10), + ), + ), + ); + + case "Call": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: TextFormField( + controller: controller, + decoration: InputDecoration( + hintText: "Enter procedure here", + labelText: "Enter procedure here", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(10), + ), + ), + ); + + case "Register": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: TextFormField( + controller: controller, + decoration: InputDecoration( + hintText: "Enter procedure here", + labelText: "Enter procedure here", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(10), + ), + ), + ); + + case "Subscribe": + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: TextFormField( + controller: controller, + decoration: InputDecoration( + hintText: "Enter topic here", + labelText: "Enter topic here", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.all(10), + ), + ), + ); + + default: + return Container(); + } + } + + // Build Args Widget + Widget buildArgs(String argsSendButton) { + switch (argsSendButton) { + case "Publish": + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Padding( + padding: EdgeInsets.only(left: 10), + child: Align( + alignment: Alignment.topLeft, + child: Text( + "Args", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ), + ), + IconButton( + onPressed: () { + Provider.of( + context, + listen: false, + ).addController(); + }, + icon: const Icon( + Icons.add_box_sharp, + size: 24, + ), + ), + ], + ), + ], + ), + ), + const ArgsTextFormFields(), + ], + ); + + case "Call": + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Padding( + padding: EdgeInsets.only(left: 10), + child: Align( + alignment: Alignment.topLeft, + child: Text( + "Args", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ), + ), + IconButton( + onPressed: () { + Provider.of( + context, + listen: false, + ).addController(); + }, + icon: const Icon( + Icons.add_box_sharp, + size: 24, + ), + ), + ], + ), + ], + ), + ), + const ArgsTextFormFields(), + ], + ); + + default: + return Container(); + } + } + + // BUILD Kwargs Widget + Widget buildKwargs(String kWargSendButton) { + switch (kWargSendButton) { + case "Publish": + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Padding( + padding: EdgeInsets.only(left: 10), + child: Align( + alignment: Alignment.topLeft, + child: Text( + "Kwargs", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ), + ), + IconButton( + onPressed: () { + Provider.of( + context, + listen: false, + ).addRow({ + "key": "", + "value": "", + }); + }, + icon: const Icon( + Icons.add_box_sharp, + size: 24, + ), + ), + ], + ), + ), + const DynamicKeyValuePairs(), + ], + ); + + case "Call": + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Padding( + padding: EdgeInsets.only(left: 10), + child: Align( + alignment: Alignment.topLeft, + child: Text( + "Kwargs", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ), + ), + IconButton( + onPressed: () { + Provider.of( + context, + listen: false, + ).addRow({ + "key": "", + "value": "", + }); + }, + icon: const Icon( + Icons.add_box_sharp, + size: 24, + ), + ), + ], + ), + ), + const DynamicKeyValuePairs(), + ], + ); + + default: + return Container(); + } + } +} diff --git a/lib/utils/args_screen.dart b/lib/utils/args_screen.dart new file mode 100644 index 0000000..0feec5c --- /dev/null +++ b/lib/utils/args_screen.dart @@ -0,0 +1,56 @@ +import "package:flutter/material.dart"; +import "package:provider/provider.dart"; +import "package:xconn_ui/Providers/args_provider.dart"; +import "package:xconn_ui/constants.dart"; + +class ArgsTextFormFields extends StatelessWidget { + const ArgsTextFormFields({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, model, _) { + return SizedBox( + height: 120, + child: ListView.builder( + shrinkWrap: true, + itemCount: model.controllers.length, + itemBuilder: (context, index) { + return ListTile( + title: SizedBox( + height: 45, + child: TextFormField( + controller: model.controllers[index], + decoration: InputDecoration( + labelText: "Enter args here", + labelStyle: TextStyle(color: blackColor), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: blackColor), + borderRadius: BorderRadius.circular(8), + ), + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + trailing: InkWell( + hoverColor: Colors.blue.shade200, + onTap: () => model.removeController(index), + child: Icon( + Icons.delete, + color: closeIconColor, + ), + ), + ); + }, + ), + ); + }, + ); + } +} diff --git a/lib/utils/kwargs_screen.dart b/lib/utils/kwargs_screen.dart new file mode 100644 index 0000000..4072071 --- /dev/null +++ b/lib/utils/kwargs_screen.dart @@ -0,0 +1,151 @@ +import "package:flutter/foundation.dart"; +import "package:flutter/material.dart"; +import "package:provider/provider.dart"; +import "package:xconn_ui/Providers/kwargs_provider.dart"; + +class DynamicKeyValuePairs extends StatelessWidget { + const DynamicKeyValuePairs({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, tableProvider, _) { + // Extract key-value pairs from tableProvider.tableData + Map kWarValues = {}; + + for (final map in tableProvider.tableData) { + String key = map["key"]; + dynamic value = map["value"]; + if (key.isNotEmpty) { + kWarValues[key] = value; + } + } + + return SizedBox( + height: 200, + child: SingleChildScrollView( + child: TableWidget(tableProvider.tableData), + ), + ); + }, + ); + } +} + +class TableWidget extends StatefulWidget { + const TableWidget(this.tableData, {super.key}); + final List> tableData; + + @override + State createState() => _TableWidgetState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(IterableProperty>("tableData", tableData)); + } +} + +class _TableWidgetState extends State { + TableRow _buildTableRow( + Map rowData, + int index, + KwargsProvider kWarProvider, + ) { + return TableRow( + children: [ + _buildTableCell( + TextFormField( + initialValue: rowData["key"], + onChanged: (newValue) { + rowData["key"] = newValue; + }, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.all(8), + ), + ), + ), + _buildTableCell( + TextFormField( + initialValue: rowData["value"], + onChanged: (newValue) { + rowData["value"] = newValue; + }, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.all(8), + ), + ), + ), + _buildTableCell( + IconButton( + icon: const Icon( + Icons.delete, + color: Colors.red, + ), + onPressed: () { + kWarProvider.removeRow(index); + }, + ), + ), + ], + ); + } + + TableCell _buildTableCell(Widget child) { + return TableCell( + child: Container( + alignment: Alignment.center, + height: 50, + child: child, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Table( + border: TableBorder.all(color: Colors.grey), + columnWidths: const { + 0: FixedColumnWidth(150), + 1: FixedColumnWidth(150), + 2: FixedColumnWidth(50), + }, + children: [ + TableRow( + decoration: BoxDecoration( + color: Colors.grey[300], + ), + children: [ + _buildTableCell( + const Text( + "Key", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + _buildTableCell( + const Text( + "Value", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + _buildTableCell( + const Text( + "", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ], + ), + ...widget.tableData.asMap().entries.map( + (entry) => _buildTableRow( + entry.value, + entry.key, + Provider.of(context, listen: false), + ), + ), + ], + ); + } +} diff --git a/lib/utils/tab_data_class.dart b/lib/utils/tab_data_class.dart new file mode 100644 index 0000000..5ee26f4 --- /dev/null +++ b/lib/utils/tab_data_class.dart @@ -0,0 +1,27 @@ +import "package:flutter/cupertino.dart"; + +class TabData { + TabData() { + linkController.addListener(_linkControllerListener); + realmController.addListener(_realmControllerListener); + topicProcedureController.addListener(_topicProcedureControllerListener); + } + String selectedValue = ""; + String selectedSerializer = ""; + String sendButtonText = "Send"; + TextEditingController linkController = TextEditingController(); + TextEditingController realmController = TextEditingController(); + TextEditingController topicProcedureController = TextEditingController(); + + void disposeControllers() { + linkController.dispose(); + realmController.dispose(); + topicProcedureController.dispose(); + } + + void _linkControllerListener() {} + + void _realmControllerListener() {} + + void _topicProcedureControllerListener() {} +} diff --git a/pubspec.yaml b/pubspec.yaml index 78aa2ce..f165452 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,9 +31,13 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 + dynamic_tabbar: ^1.0.7 flutter: sdk: flutter + provider: ^6.1.2 + wampproto: ^0.1.0 + xconn: ^0.1.0 dev_dependencies: # The "flutter_lints" package below contains a set of recommended lints to diff --git a/test/widget_test.dart b/test/widget_test.dart index beff3e5..3bb574c 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -4,6 +4,8 @@ // utility in the flutter_test package. For example, you can send tap and scroll // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. +@Skip("skipping for now") +library; import "package:flutter/material.dart"; import "package:flutter_test/flutter_test.dart";