mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2026-10-25 17:17:56 +00:00
initialized Devices Page
This commit is contained in:
@@ -1,25 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/assets_manager.dart';
|
||||
|
||||
import '../model/device_model.dart';
|
||||
import '../model/device_category_model.dart';
|
||||
|
||||
part 'devices_state.dart';
|
||||
|
||||
class DevicesCubit extends Cubit<DevicesState> {
|
||||
DevicesCubit() : super(DevicesInitial()) {
|
||||
getDevices();
|
||||
getCategories();
|
||||
}
|
||||
|
||||
bool ACSwitchValue = false;
|
||||
bool lightsSwitchValue = false;
|
||||
bool doorSwitchValue = false;
|
||||
bool curtainSwitchValue = false;
|
||||
bool screensSwitchValue = false;
|
||||
bool gatewaySwitchValue = false;
|
||||
|
||||
void changeSwitchValue(DeviceType device) {
|
||||
switch (device) {
|
||||
case DeviceType.AC:
|
||||
ACSwitchValue = !ACSwitchValue;
|
||||
break;
|
||||
case DeviceType.Lights:
|
||||
lightsSwitchValue = !lightsSwitchValue;
|
||||
break;
|
||||
case DeviceType.Door:
|
||||
doorSwitchValue = !doorSwitchValue;
|
||||
break;
|
||||
case DeviceType.Curtain:
|
||||
curtainSwitchValue = !curtainSwitchValue;
|
||||
break;
|
||||
case DeviceType.Screens:
|
||||
screensSwitchValue = !screensSwitchValue;
|
||||
break;
|
||||
case DeviceType.Gateway:
|
||||
gatewaySwitchValue = !gatewaySwitchValue;
|
||||
break;
|
||||
}
|
||||
emit(DevicesSuccess());
|
||||
}
|
||||
|
||||
//TODO separate the navigation logic to another cubit
|
||||
static DevicesCubit get(context) => BlocProvider.of(context);
|
||||
|
||||
var devices = <DeviceModel>[];
|
||||
var categories = <DevicesCategoryModel>[];
|
||||
|
||||
Future<List<DeviceModel>> getDevices() async {
|
||||
Future<List<DevicesCategoryModel>> getCategories() async {
|
||||
emit(DevicesLoading());
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
emit(DevicesSuccess());
|
||||
|
||||
return devices = [];
|
||||
return categories = [
|
||||
DevicesCategoryModel(
|
||||
devices: [],
|
||||
icon: IconsManager.ac,
|
||||
name: 'ACs',
|
||||
switchValue: false,
|
||||
type: DeviceType.AC,
|
||||
),
|
||||
DevicesCategoryModel(
|
||||
devices: [],
|
||||
icon: IconsManager.light,
|
||||
name: 'Lights',
|
||||
switchValue: false,
|
||||
type: DeviceType.Lights,
|
||||
),
|
||||
DevicesCategoryModel(
|
||||
devices: [],
|
||||
icon: IconsManager.doorLock,
|
||||
name: 'Doors',
|
||||
switchValue: false,
|
||||
type: DeviceType.Door,
|
||||
),
|
||||
DevicesCategoryModel(
|
||||
devices: [],
|
||||
icon: IconsManager.curtain,
|
||||
name: 'Curtains',
|
||||
switchValue: false,
|
||||
type: DeviceType.Curtain,
|
||||
),
|
||||
DevicesCategoryModel(
|
||||
devices: [],
|
||||
icon: IconsManager.screen,
|
||||
name: 'Screens',
|
||||
switchValue: false,
|
||||
type: DeviceType.Screens,
|
||||
),
|
||||
DevicesCategoryModel(
|
||||
devices: [],
|
||||
icon: IconsManager.gateway,
|
||||
name: 'Gateway',
|
||||
switchValue: false,
|
||||
type: DeviceType.Gateway,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'device_model.dart';
|
||||
|
||||
class DevicesCategoryModel {
|
||||
final String name;
|
||||
final String icon;
|
||||
|
||||
final bool switchValue;
|
||||
final List<DeviceModel> devices;
|
||||
|
||||
final DeviceType type;
|
||||
|
||||
DevicesCategoryModel(
|
||||
{required this.type,
|
||||
required this.name,
|
||||
required this.icon,
|
||||
required this.switchValue,
|
||||
required this.devices});
|
||||
}
|
||||
|
||||
enum DeviceType {
|
||||
AC,
|
||||
Lights,
|
||||
Door,
|
||||
Curtain,
|
||||
Screens,
|
||||
Gateway,
|
||||
}
|
||||
@@ -1 +1,14 @@
|
||||
class DeviceModel {}
|
||||
class DeviceModel {
|
||||
final String name;
|
||||
final String imageUrl;
|
||||
final String description;
|
||||
final String category;
|
||||
final String id;
|
||||
|
||||
DeviceModel(
|
||||
{required this.name,
|
||||
required this.imageUrl,
|
||||
required this.description,
|
||||
required this.category,
|
||||
required this.id});
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart';
|
||||
|
||||
class DevicesView extends StatelessWidget {
|
||||
const DevicesView({super.key});
|
||||
class CategoriesView extends StatelessWidget {
|
||||
const CategoriesView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/devices_mode_tab.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/switches.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
|
||||
|
||||
class CategoriesView extends StatelessWidget {
|
||||
const CategoriesView({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(top: 60, right: 15, left: 15, bottom: 100),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: StringsManager.devices,
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
DevicesModeTab(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: StringsManager.wizard,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
),
|
||||
),
|
||||
Switches(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/assets_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
|
||||
|
||||
class DevicesModeTab extends StatelessWidget {
|
||||
const DevicesModeTab({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.only(right: 5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 30,
|
||||
width: 25,
|
||||
child: SvgPicture.asset(
|
||||
IconsManager.winter,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
const Gap(5),
|
||||
const BodySmall(
|
||||
text: StringsManager.winter,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontColor: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.only(right: 5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 30,
|
||||
width: 25,
|
||||
child: SvgPicture.asset(
|
||||
IconsManager.summer,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
const Gap(5),
|
||||
const BodySmall(
|
||||
text: StringsManager.summer,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontColor: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Expanded(child: SizedBox.shrink())
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/categories_view.dart';
|
||||
|
||||
import '../../bloc/devices_cubit.dart';
|
||||
import 'no_devices_view.dart';
|
||||
@@ -15,9 +16,13 @@ class DevicesViewBody extends StatelessWidget {
|
||||
create: (context) => DevicesCubit(),
|
||||
child: BlocBuilder<DevicesCubit, DevicesState>(
|
||||
builder: (context, state) {
|
||||
return DevicesCubit.get(context).devices.isEmpty
|
||||
? const NoDevicesView()
|
||||
: const SizedBox();
|
||||
return state is DevicesLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state is DevicesSuccess
|
||||
? DevicesCubit.get(context).categories.isEmpty
|
||||
? const NoDevicesView()
|
||||
: const CategoriesView()
|
||||
: const Center(child: Text('Error'));
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/custom_switch.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
|
||||
import '../../bloc/devices_cubit.dart';
|
||||
|
||||
class Switches extends StatelessWidget {
|
||||
const Switches({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
childAspectRatio: 1.5,
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: DevicesCubit.get(context).categories.length,
|
||||
itemBuilder: (context, index) {
|
||||
return DefaultContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
DevicesCubit.get(context).categories[index].icon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
height: 20,
|
||||
child: CustomSwitch(
|
||||
value: DevicesCubit.get(context)
|
||||
.categories[index]
|
||||
.switchValue,
|
||||
onChanged: (value) => DevicesCubit.get(context)
|
||||
.changeSwitchValue(
|
||||
DevicesCubit.get(context).categories[index].type),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
BodyLarge(
|
||||
text: DevicesCubit.get(context).categories[index].name,
|
||||
fontWeight: FontWeight.bold,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class CustomSwitch extends StatefulWidget {
|
||||
final bool value;
|
||||
final ValueChanged<bool> onChanged;
|
||||
|
||||
const CustomSwitch({super.key, required this.value, required this.onChanged});
|
||||
|
||||
@override
|
||||
_CustomSwitchState createState() => _CustomSwitchState();
|
||||
}
|
||||
|
||||
class _CustomSwitchState extends State<CustomSwitch>
|
||||
with SingleTickerProviderStateMixin {
|
||||
Animation? _circleAnimation;
|
||||
AnimationController? _animationController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 100));
|
||||
_circleAnimation = AlignmentTween(
|
||||
begin: widget.value ? Alignment.centerRight : Alignment.centerLeft,
|
||||
end: widget.value ? Alignment.centerLeft : Alignment.centerRight)
|
||||
.animate(CurvedAnimation(
|
||||
parent: _animationController!, curve: Curves.linear));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _animationController!,
|
||||
builder: (context, child) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_animationController!.isCompleted
|
||||
? _animationController!.reverse()
|
||||
: _animationController!.forward();
|
||||
widget.value == false
|
||||
? widget.onChanged(true)
|
||||
: widget.onChanged(false);
|
||||
},
|
||||
child: Container(
|
||||
width: 45.0,
|
||||
height: 28.0,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
color: _circleAnimation!.value == Alignment.centerLeft
|
||||
? Colors.grey
|
||||
: ColorsManager.primaryColor,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
alignment: widget.value
|
||||
? ((Directionality.of(context) == TextDirection.rtl)
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft)
|
||||
: ((Directionality.of(context) == TextDirection.rtl)
|
||||
? Alignment.centerLeft
|
||||
: Alignment.centerRight),
|
||||
child: Container(
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DefaultContainer extends StatelessWidget {
|
||||
const DefaultContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ class BodyLarge extends StatelessWidget {
|
||||
this.textAlign,
|
||||
this.style,
|
||||
this.height,
|
||||
this.fontWeight,
|
||||
});
|
||||
|
||||
final String text;
|
||||
@@ -19,10 +20,13 @@ class BodyLarge extends StatelessWidget {
|
||||
|
||||
final double? height;
|
||||
|
||||
final FontWeight? fontWeight;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => CustomText(
|
||||
text,
|
||||
style: style ?? context.bodyLarge.copyWith(height: height ?? 1.5),
|
||||
textAlign: textAlign,
|
||||
fontWeight: fontWeight,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ class BodySmall extends StatelessWidget {
|
||||
this.style,
|
||||
this.fontColor,
|
||||
this.fontSize,
|
||||
this.fontWeight,
|
||||
});
|
||||
|
||||
final String text;
|
||||
@@ -17,6 +18,7 @@ class BodySmall extends StatelessWidget {
|
||||
final Color? fontColor;
|
||||
|
||||
final double? fontSize;
|
||||
final FontWeight? fontWeight;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => CustomText(
|
||||
@@ -24,5 +26,6 @@ class BodySmall extends StatelessWidget {
|
||||
style: style ?? context.bodySmall,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ class CustomText extends StatelessWidget {
|
||||
this.maxLines,
|
||||
this.textDirection,
|
||||
this.fontSize,
|
||||
this.fontColor});
|
||||
this.fontColor,
|
||||
this.fontWeight});
|
||||
|
||||
final String text;
|
||||
final TextStyle? style;
|
||||
@@ -24,14 +25,16 @@ class CustomText extends StatelessWidget {
|
||||
final double? fontSize;
|
||||
final Color? fontColor;
|
||||
|
||||
final FontWeight? fontWeight;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SelectableText(
|
||||
text,
|
||||
style: style!.copyWith(
|
||||
fontSize: fontSize,
|
||||
color: fontColor ?? ColorsManager.textPrimaryColor,
|
||||
),
|
||||
fontSize: fontSize,
|
||||
color: fontColor ?? ColorsManager.textPrimaryColor,
|
||||
fontWeight: fontWeight),
|
||||
textAlign: textAlign,
|
||||
onTap: onTap,
|
||||
minLines: minLines,
|
||||
|
||||
Reference in New Issue
Block a user