mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
reduce duplicated code
This commit is contained in:
@ -5,11 +5,15 @@ class CustomExpansionTile extends StatefulWidget {
|
||||
final String title;
|
||||
final List<Widget>? children;
|
||||
final bool initiallyExpanded;
|
||||
final bool? isExpanded; // New parameter to control expansion
|
||||
final ValueChanged<bool>? onExpansionChanged; // Callback for expansion change
|
||||
|
||||
CustomExpansionTile({
|
||||
required this.title,
|
||||
this.children,
|
||||
this.initiallyExpanded = false,
|
||||
this.isExpanded, // Allow external control over expansion
|
||||
this.onExpansionChanged, // Notify when expansion changes
|
||||
});
|
||||
|
||||
@override
|
||||
@ -26,6 +30,16 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
||||
_isExpanded = widget.initiallyExpanded;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CustomExpansionTile oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.isExpanded != null && widget.isExpanded != _isExpanded) {
|
||||
setState(() {
|
||||
_isExpanded = widget.isExpanded!;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@ -34,6 +48,7 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isExpanded = !_isExpanded;
|
||||
widget.onExpansionChanged?.call(_isExpanded);
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
@ -47,28 +62,22 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
||||
});
|
||||
},
|
||||
side: WidgetStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const BorderSide(color: ColorsManager.grayBorder);
|
||||
}
|
||||
return const BorderSide(color: ColorsManager.grayBorder);
|
||||
}),
|
||||
// Customize the color for different checkbox states
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return ColorsManager.grayBorder; // Checked state color
|
||||
return ColorsManager.grayBorder;
|
||||
} else {
|
||||
return ColorsManager.checkBoxFillColor;
|
||||
}
|
||||
return ColorsManager
|
||||
.checkBoxFillColor; // Unchecked state color
|
||||
}),
|
||||
checkColor: ColorsManager.whiteColors, // Checkmark color
|
||||
checkColor: ColorsManager.whiteColors,
|
||||
),
|
||||
// Expand/Collapse Icon with reduced size
|
||||
if (widget.children != null && widget.children!.isNotEmpty)
|
||||
Icon(
|
||||
_isExpanded
|
||||
? Icons.keyboard_arrow_down // Upward arrow when expanded
|
||||
: Icons
|
||||
.keyboard_arrow_right, // Right arrow when collapsed
|
||||
? Icons.keyboard_arrow_down
|
||||
: Icons.keyboard_arrow_right,
|
||||
color: Colors.grey,
|
||||
size: 16.0, // Reduced icon size
|
||||
),
|
||||
@ -87,7 +96,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
||||
],
|
||||
),
|
||||
),
|
||||
// Display children if available and expanded
|
||||
if (_isExpanded &&
|
||||
widget.children != null &&
|
||||
widget.children!.isNotEmpty)
|
||||
|
70
lib/common/search_bar.dart
Normal file
70
lib/common/search_bar.dart
Normal file
@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CustomSearchBar extends StatelessWidget {
|
||||
final TextEditingController? controller;
|
||||
final String hintText;
|
||||
|
||||
const CustomSearchBar({
|
||||
Key? key,
|
||||
this.controller,
|
||||
this.hintText = 'Search',
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
spreadRadius: 0,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: ColorsManager.textFieldGreyColor,
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: SvgPicture.asset(
|
||||
Assets.textFieldSearch,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
horizontal: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -59,6 +59,7 @@ class MyApp extends StatelessWidget {
|
||||
bodyLarge: TextStyle(fontSize: 16, color: Colors.white),
|
||||
headlineSmall: TextStyle(color: Colors.black87, fontSize: 18),
|
||||
headlineMedium: TextStyle(color: Colors.black87, fontSize: 20),
|
||||
titleMedium: TextStyle(color: Colors.black87, fontSize: 22, fontWeight: FontWeight.bold ),
|
||||
headlineLarge: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
|
29
lib/pages/spaces_management/view/community_tile.dart
Normal file
29
lib/pages/spaces_management/view/community_tile.dart
Normal file
@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/common/custom_expansion_tile.dart';
|
||||
|
||||
class CommunityTile extends StatelessWidget {
|
||||
final String title;
|
||||
final String expandedTile;
|
||||
final Function(String, bool) onExpansionChanged;
|
||||
final List<Widget>? children;
|
||||
|
||||
const CommunityTile({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.expandedTile,
|
||||
required this.onExpansionChanged,
|
||||
this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomExpansionTile(
|
||||
title: title,
|
||||
isExpanded: expandedTile == title,
|
||||
onExpansionChanged: (bool expanded) {
|
||||
onExpansionChanged(title, expanded);
|
||||
},
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
}
|
107
lib/pages/spaces_management/view/sidebar_widget.dart
Normal file
107
lib/pages/spaces_management/view/sidebar_widget.dart
Normal file
@ -0,0 +1,107 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/common/search_bar.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class SidebarWidget extends StatefulWidget {
|
||||
final Function(String)? onCommunitySelected;
|
||||
|
||||
SidebarWidget({this.onCommunitySelected});
|
||||
|
||||
@override
|
||||
_SidebarWidgetState createState() => _SidebarWidgetState();
|
||||
}
|
||||
|
||||
class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
String? _expandedTile;
|
||||
|
||||
// A helper method to handle the expansion logic for CustomExpansionTile
|
||||
void _handleExpansionChange(String title, bool expanded) {
|
||||
setState(() {
|
||||
_expandedTile = expanded ? title : null;
|
||||
});
|
||||
widget.onCommunitySelected?.call(title);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 300,
|
||||
decoration: subSectionContainerDecoration,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Communities title with the add button
|
||||
Container(
|
||||
decoration: subSectionContainerDecoration,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Communities',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Handle add button action
|
||||
},
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.roundedAddIcon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Search bar
|
||||
const CustomSearchBar(),
|
||||
const SizedBox(height: 16),
|
||||
// Community list with one item expanded at a time
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
CommunityTile(
|
||||
title: "Downtown Dubai",
|
||||
expandedTile: _expandedTile ?? '',
|
||||
onExpansionChanged: _handleExpansionChange,
|
||||
),
|
||||
CommunityTile(
|
||||
title: 'Dubai Creek Harbour',
|
||||
expandedTile: _expandedTile ?? '',
|
||||
onExpansionChanged: _handleExpansionChange,
|
||||
),
|
||||
CommunityTile(
|
||||
title: 'Dubai Hills Estate',
|
||||
expandedTile: _expandedTile ?? '',
|
||||
onExpansionChanged: _handleExpansionChange,
|
||||
children: [
|
||||
CommunityTile(
|
||||
title: 'South Side',
|
||||
expandedTile: _expandedTile ?? '',
|
||||
onExpansionChanged: _handleExpansionChange,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/common/custom_expansion_tile.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
@ -18,6 +19,11 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
List<SpaceData> spaces = [];
|
||||
List<Connection> connections = [];
|
||||
|
||||
void _handleCommunitySelection(String community) {
|
||||
// Handle community selection here
|
||||
print("Selected Community: $community");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Size screenSize = MediaQuery.of(context).size;
|
||||
@ -33,156 +39,9 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
Row(
|
||||
children: [
|
||||
// Sidebar for navigation and community list
|
||||
Container(
|
||||
width: 300, // Fixed width for sidebar
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, // No gradient inside
|
||||
SidebarWidget(
|
||||
onCommunitySelected: _handleCommunitySelection,
|
||||
),
|
||||
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// "Communities" title with the add button
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, // Background color
|
||||
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 1,
|
||||
offset: Offset(0, 2), // Adjust the shadow position
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(
|
||||
16.0), // Apply padding inside the container
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Communities',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
// Plus Button (+)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Handle the button tap action
|
||||
},
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets
|
||||
.roundedAddIcon, // Path to your SVG asset
|
||||
width: 24, // Set the width
|
||||
height: 24, // Set the height
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Search bar
|
||||
// Search bar
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
Colors.black.withOpacity(0.2), // Subtle shadow
|
||||
spreadRadius: 0, // No spread
|
||||
blurRadius: 8, // Softer shadow edges
|
||||
offset: Offset(0, 4), // Shadow only on the bottom
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
width: double
|
||||
.infinity, // Make the container take the full width of its parent
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
12), // Smooth rounded corners
|
||||
),
|
||||
child: TextField(
|
||||
style: const TextStyle(
|
||||
color: Colors.black, // Set the text color to black
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: ColorsManager
|
||||
.textFieldGreyColor, // Background color for the text field itself
|
||||
hintText: 'Search',
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.grey[400], // Gray hint text color
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right:
|
||||
16), // Add padding to avoid icon sticking to the edge
|
||||
child: SvgPicture.asset(
|
||||
Assets
|
||||
.textFieldSearch, // Path to your SVG asset
|
||||
width: 24, // Set the width
|
||||
height: 24, // Set the height
|
||||
),
|
||||
), // Search icon
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none, // Remove border
|
||||
borderRadius: BorderRadius.circular(
|
||||
12), // Rounded corners for text field
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical:
|
||||
14, // Vertical padding for proper text alignment
|
||||
horizontal:
|
||||
16, // Add space between the text and the left edge
|
||||
), // Proper padding for alignment
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
// Example of community list
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
CustomExpansionTile(
|
||||
title: "Downtown Dubai",
|
||||
),
|
||||
CustomExpansionTile(
|
||||
title: 'Dubai Creek Harbour',
|
||||
),
|
||||
CustomExpansionTile(
|
||||
title: 'Dubai Hills Estate',
|
||||
children: [
|
||||
CustomExpansionTile(
|
||||
title: 'South Side',
|
||||
initiallyExpanded: false),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Right Side: Community Structure Area
|
||||
Expanded(
|
||||
child: Container(
|
||||
|
@ -41,3 +41,15 @@ BoxDecoration containerDecoration = BoxDecoration(
|
||||
],
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10)));
|
||||
|
||||
BoxDecoration subSectionContainerDecoration = BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 1,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
Reference in New Issue
Block a user