mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
reduce duplicated code
This commit is contained in:
@ -5,11 +5,15 @@ class CustomExpansionTile extends StatefulWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final List<Widget>? children;
|
final List<Widget>? children;
|
||||||
final bool initiallyExpanded;
|
final bool initiallyExpanded;
|
||||||
|
final bool? isExpanded; // New parameter to control expansion
|
||||||
|
final ValueChanged<bool>? onExpansionChanged; // Callback for expansion change
|
||||||
|
|
||||||
CustomExpansionTile({
|
CustomExpansionTile({
|
||||||
required this.title,
|
required this.title,
|
||||||
this.children,
|
this.children,
|
||||||
this.initiallyExpanded = false,
|
this.initiallyExpanded = false,
|
||||||
|
this.isExpanded, // Allow external control over expansion
|
||||||
|
this.onExpansionChanged, // Notify when expansion changes
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -26,6 +30,16 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
|||||||
_isExpanded = widget.initiallyExpanded;
|
_isExpanded = widget.initiallyExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CustomExpansionTile oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.isExpanded != null && widget.isExpanded != _isExpanded) {
|
||||||
|
setState(() {
|
||||||
|
_isExpanded = widget.isExpanded!;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
@ -34,6 +48,7 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isExpanded = !_isExpanded;
|
_isExpanded = !_isExpanded;
|
||||||
|
widget.onExpansionChanged?.call(_isExpanded);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -47,28 +62,22 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
side: WidgetStateBorderSide.resolveWith((states) {
|
side: WidgetStateBorderSide.resolveWith((states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
|
||||||
return const BorderSide(color: ColorsManager.grayBorder);
|
|
||||||
}
|
|
||||||
return const BorderSide(color: ColorsManager.grayBorder);
|
return const BorderSide(color: ColorsManager.grayBorder);
|
||||||
}),
|
}),
|
||||||
// Customize the color for different checkbox states
|
|
||||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
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)
|
if (widget.children != null && widget.children!.isNotEmpty)
|
||||||
Icon(
|
Icon(
|
||||||
_isExpanded
|
_isExpanded
|
||||||
? Icons.keyboard_arrow_down // Upward arrow when expanded
|
? Icons.keyboard_arrow_down
|
||||||
: Icons
|
: Icons.keyboard_arrow_right,
|
||||||
.keyboard_arrow_right, // Right arrow when collapsed
|
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
size: 16.0, // Reduced icon size
|
size: 16.0, // Reduced icon size
|
||||||
),
|
),
|
||||||
@ -87,7 +96,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Display children if available and expanded
|
|
||||||
if (_isExpanded &&
|
if (_isExpanded &&
|
||||||
widget.children != null &&
|
widget.children != null &&
|
||||||
widget.children!.isNotEmpty)
|
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),
|
bodyLarge: TextStyle(fontSize: 16, color: Colors.white),
|
||||||
headlineSmall: TextStyle(color: Colors.black87, fontSize: 18),
|
headlineSmall: TextStyle(color: Colors.black87, fontSize: 18),
|
||||||
headlineMedium: TextStyle(color: Colors.black87, fontSize: 20),
|
headlineMedium: TextStyle(color: Colors.black87, fontSize: 20),
|
||||||
|
titleMedium: TextStyle(color: Colors.black87, fontSize: 22, fontWeight: FontWeight.bold ),
|
||||||
headlineLarge: TextStyle(
|
headlineLarge: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 24,
|
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/common/custom_expansion_tile.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/add_space_button.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/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/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
@ -18,6 +19,11 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
|||||||
List<SpaceData> spaces = [];
|
List<SpaceData> spaces = [];
|
||||||
List<Connection> connections = [];
|
List<Connection> connections = [];
|
||||||
|
|
||||||
|
void _handleCommunitySelection(String community) {
|
||||||
|
// Handle community selection here
|
||||||
|
print("Selected Community: $community");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Size screenSize = MediaQuery.of(context).size;
|
Size screenSize = MediaQuery.of(context).size;
|
||||||
@ -33,156 +39,9 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
// Sidebar for navigation and community list
|
// Sidebar for navigation and community list
|
||||||
Container(
|
SidebarWidget(
|
||||||
width: 300, // Fixed width for sidebar
|
onCommunitySelected: _handleCommunitySelection,
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white, // No gradient inside
|
|
||||||
),
|
),
|
||||||
|
|
||||||
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
|
// Right Side: Community Structure Area
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -41,3 +41,15 @@ BoxDecoration containerDecoration = BoxDecoration(
|
|||||||
],
|
],
|
||||||
color: ColorsManager.boxColor,
|
color: ColorsManager.boxColor,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(10)));
|
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