restructured the application

added .end files
switched to BLoC state-management
removed unnecessary helpers
initialized Scenes Page
This commit is contained in:
Mohammad Salameh
2024-02-15 14:00:09 +03:00
parent 16f47f744c
commit 1d18210158
79 changed files with 554 additions and 723 deletions
-16
View File
@@ -1,16 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/auth/models/user_model.dart';
class AuthProvider extends ChangeNotifier {
// todo remove temp user
UserModel user = UserModel(
id: '1',
name: 'John Doe',
email: '',
photoUrl: '',
isAgreementAccepted: false,
isAnonymous: false,
isEmailVerified: false,
phoneNumber: '',
);
}
-58
View File
@@ -1,58 +0,0 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:provider/provider.dart';
import 'package:syncrow_app/features/home/widgets/syncrow_logo.dart';
import 'package:syncrow_app/features/shared_widgets/default_text_button.dart';
import 'package:syncrow_app/navigation/route_manager.dart';
import '../../navigation/routing_constants.dart';
import 'auth_provider.dart';
class AuthPage extends StatelessWidget {
const AuthPage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => AuthProvider(),
builder: (context, child) => Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Expanded(child: SizedBox()),
const SyncrowLogo(),
const Expanded(flex: 2, child: SizedBox()),
DefaultTextButton(
text: 'Login',
onPressed: () {
RouteManager().routerManagerPopAndPushNamed(
routeName: Routes.homeRoute,
context: context,
);
},
isPrimary: true,
),
const Gap(15),
const DefaultTextButton(text: 'Sign Up'),
const Gap(20),
Center(
child: InkWell(
onTap: () {},
child: const Text(
'Try as a Guest',
style: TextStyle(
color: Colors.grey,
),
),
),
),
const Gap(30),
],
),
),
),
);
}
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'auth_state.dart';
class AuthCubit extends Cubit<AuthState> {
AuthCubit() : super(AuthInitial());
}
+6
View File
@@ -0,0 +1,6 @@
part of 'auth_cubit.dart';
@immutable
abstract class AuthState {}
class AuthInitial extends AuthState {}
+19
View File
@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/auth/view/widgets/auth_view_body.dart';
class AuthView extends StatelessWidget {
const AuthView({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
return const Scaffold(
body: AuthViewBody(),
);
},
);
}
}
@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:syncrow_app/features/shared_widgets/default_text_button.dart';
import 'package:syncrow_app/features/shared_widgets/syncrow_logo.dart';
import '../../../../navigation/routing_constants.dart';
class AuthViewBody extends StatelessWidget {
const AuthViewBody({
super.key,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Expanded(child: SizedBox()),
const SyncrowLogo(),
const Expanded(flex: 2, child: SizedBox()),
DefaultTextButton(
text: 'Login',
onPressed: () {
Navigator.popAndPushNamed(context, Routes.homeRoute);
},
),
const Gap(15),
const DefaultTextButton(
text: 'Sign Up',
isSecondary: true,
),
const Gap(20),
Center(
child: InkWell(
onTap: () {},
child: const Text(
'Try as a Guest',
style: TextStyle(
color: Colors.grey,
),
),
),
),
const Gap(30),
],
),
);
}
}
@@ -1,29 +1,31 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/home/model/device_model.dart';
import 'package:syncrow_app/features/profile/profile_view.dart';
import 'package:syncrow_app/features/scene/scene_view.dart';
import 'package:syncrow_app/features/smart/smart_view.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'home_state.dart';
import 'widgets/home_view_body.dart';
import '../../profile/view/profile_view.dart';
import '../../scene/view/scene_view.dart';
import '../../smart/view/smart_view.dart';
import '../model/device_model.dart';
import '../view/widgets/home_view_body.dart';
class HomeProvider extends ChangeNotifier {
HomeProvider() {
print('HomeProvider created');
part 'home_state.dart';
class HomeCubit extends Cubit<HomeState> {
HomeCubit() : super(HomeInitial()) {
getDevices();
}
HomeState state = HomeState();
//TODO separate the navigation logic to another cubit
static HomeCubit get(context) => BlocProvider.of(context);
static int pageIndex = 0;
var devices = <DeviceModel>[];
Future<List<DeviceModel>> getDevices() async {
state.setLoading();
emit(HomeLoading());
await Future.delayed(const Duration(seconds: 2));
state.setSuccess();
notifyListeners();
emit(HomeSuccess());
return devices = [];
}
@@ -64,19 +66,15 @@ class HomeProvider extends ChangeNotifier {
final List<Widget> pages = [
const HomeViewBody(),
const ScenePage(),
const SceneView(),
const SmartPage(),
const ProfilePage(),
const ProfileView(),
];
Widget getCurrentPage() {
notifyListeners();
return pages[pageIndex];
}
static Widget get currentPage => HomeCubit().pages[pageIndex];
void updatePageIndex(int index, BuildContext context) {
emit(HomeChangeIndex());
pageIndex = index;
notifyListeners();
}
}
+14
View File
@@ -0,0 +1,14 @@
part of 'home_cubit.dart';
@immutable
abstract class HomeState {}
class HomeInitial extends HomeState {}
class HomeLoading extends HomeState {}
class HomeSuccess extends HomeState {}
class HomeFailure extends HomeState {}
class HomeChangeIndex extends HomeState {}
-28
View File
@@ -1,28 +0,0 @@
class HomeState {
HomeState() {
loading = false;
}
late bool loading;
late bool success;
late bool error;
void setLoading() {
loading = true;
success = false;
error = false;
}
void setSuccess() {
loading = false;
success = true;
error = false;
}
void setError() {
loading = false;
success = false;
error = true;
}
}
-28
View File
@@ -1,28 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../shared_widgets/default_nav_bar.dart';
import 'home_provider.dart';
import 'widgets/default_app_bar.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => HomeProvider(),
builder: (context, child) => Consumer<HomeProvider>(
builder: (context, provider, child) {
return Scaffold(
appBar: const DefaultAppBar(),
body: provider.state.loading
? const Center(child: CircularProgressIndicator())
: provider.getCurrentPage(),
bottomNavigationBar: const DefaultNavBar(),
);
},
),
);
}
}
+28
View File
@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/home/bloc/home_cubit.dart';
import '../../shared_widgets/default_app_bar.dart';
import '../../shared_widgets/default_nav_bar.dart';
class HomeView extends StatelessWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => HomeCubit(),
child: BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
return Scaffold(
appBar: const DefaultAppBar(),
body: state is HomeLoading
? const Center(child: CircularProgressIndicator())
: HomeCubit.currentPage,
bottomNavigationBar: const DefaultNavBar(),
);
},
),
);
}
}
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/home/bloc/home_cubit.dart';
import 'home_view_no_devices.dart';
class HomeViewBody extends StatelessWidget {
const HomeViewBody({
super.key,
});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => HomeCubit(),
child: BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
return HomeCubit.get(context).devices.isEmpty
? const HomeViewNoDevices()
: const SizedBox();
},
));
}
}
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:syncrow_app/features/shared_widgets/default_text_button.dart';
import 'package:syncrow_app/utils/resource_manager/assets_manager.dart';
class HomeViewNoDevices extends StatelessWidget {
const HomeViewNoDevices({
super.key,
});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
ImageManager.boxEmpty,
opacity: const AlwaysStoppedAnimation(0.5),
scale: 1,
width: 140,
),
const Gap(15),
const Text(
'No Devices',
style: TextStyle(
color: Colors.grey,
fontSize: 18,
),
),
const Gap(15),
const DefaultTextButton(
text: 'Add Device',
),
],
),
);
}
}
@@ -1,51 +0,0 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:provider/provider.dart';
import 'package:syncrow_app/features/home/home_provider.dart';
import 'package:syncrow_app/features/shared_widgets/default_text_button.dart';
import 'package:syncrow_app/resource_manager/assets_manager.dart';
class HomeViewBody extends StatelessWidget {
const HomeViewBody({
super.key,
});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => HomeProvider(),
child: Consumer<HomeProvider>(
builder: (context, provider, child) {
return provider.devices.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
ImageManager.boxEmpty,
opacity: const AlwaysStoppedAnimation(0.5),
scale: 1,
width: 140,
),
const Gap(15),
const Text(
'No Devices',
style: TextStyle(
color: Colors.grey,
fontSize: 18,
),
),
const Gap(15),
const DefaultTextButton(
text: 'Add Device',
isPrimary: true,
),
],
),
)
: const SizedBox();
},
),
);
}
}
@@ -0,0 +1,8 @@
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'profile_state.dart';
class ProfileCubit extends Cubit<ProfileState> {
ProfileCubit() : super(ProfileInitial());
}
@@ -0,0 +1,6 @@
part of 'profile_cubit.dart';
@immutable
abstract class ProfileState {}
class ProfileInitial extends ProfileState {}
@@ -1,7 +0,0 @@
import 'package:flutter/material.dart';
import 'profile_state.dart';
class ProfileProvider extends ChangeNotifier {
final state = ProfileState();
}
-6
View File
@@ -1,6 +0,0 @@
class ProfileState {
ProfileState() {
// init some variables
}
}
-18
View File
@@ -1,18 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'profile_provider.dart';
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => ProfileProvider(),
builder: (context, child) => const Center(
child: Text('Profile Page'),
),
);
}
}
@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/profile/bloc/profile_cubit.dart';
class ProfileView extends StatelessWidget {
const ProfileView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ProfileCubit(),
child: BlocBuilder<ProfileCubit, ProfileState>(
builder: (context, state) {
return const Center(
child: Text('Profile Page'),
);
},
),
);
}
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
import 'package:syncrow_app/features/scene/model/scene_model.dart';
part 'scene_state.dart';
class SceneCubit extends Cubit<SceneState> {
SceneCubit() : super(SceneInitial()) {
getScenes();
}
static SceneCubit of(context) => BlocProvider.of<SceneCubit>(context);
void getScenes() {
emit(SceneLoading());
Future.delayed(const Duration(seconds: 5), () {
emit(SceneSuccess());
});
}
var scenes = <SceneModel>[];
}
+12
View File
@@ -0,0 +1,12 @@
part of 'scene_cubit.dart';
@immutable
abstract class SceneState {}
class SceneInitial extends SceneState {}
class SceneLoading extends SceneState {}
class SceneSuccess extends SceneState {}
class SceneFailure extends SceneState {}
+46
View File
@@ -0,0 +1,46 @@
class SceneModel {
final String id;
final String name;
final String description;
final String imageUrl;
final String location;
final String type;
final String rating;
final String price;
final String duration;
final String date;
final String time;
final String status;
SceneModel({
required this.id,
required this.name,
required this.description,
required this.imageUrl,
required this.location,
required this.type,
required this.rating,
required this.price,
required this.duration,
required this.date,
required this.time,
required this.status,
});
factory SceneModel.fromJson(Map<String, dynamic> json) {
return SceneModel(
id: json['id'],
name: json['name'],
description: json['description'],
imageUrl: json['imageUrl'],
location: json['location'],
type: json['type'],
rating: json['rating'],
price: json['price'],
duration: json['duration'],
date: json['date'],
time: json['time'],
status: json['status'],
);
}
}
-7
View File
@@ -1,7 +0,0 @@
import 'package:flutter/material.dart';
import 'scene_state.dart';
class SceneProvider extends ChangeNotifier {
final state = SceneState();
}
-6
View File
@@ -1,6 +0,0 @@
class SceneState {
SceneState() {
// init some variables
}
}
-18
View File
@@ -1,18 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'scene_provider.dart';
class ScenePage extends StatelessWidget {
const ScenePage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => SceneProvider(),
builder: (context, child) => const Center(
child: Text('Scene Page'),
),
);
}
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_cubit.dart';
import 'package:syncrow_app/features/scene/view/widgets/scene_view_no_scenes.dart';
class SceneView extends StatelessWidget {
const SceneView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => SceneCubit(),
child: BlocBuilder<SceneCubit, SceneState>(
builder: (context, state) {
return state is SceneLoading
? const Center(child: CircularProgressIndicator())
: const SceneViewNoScenes();
},
),
);
}
}
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:syncrow_app/features/shared_widgets/default_text_button.dart';
import 'package:syncrow_app/utils/resource_manager/assets_manager.dart';
class SceneViewNoScenes extends StatelessWidget {
const SceneViewNoScenes({
super.key,
});
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
ImageManager.automation,
scale: 1,
opacity: const AlwaysStoppedAnimation(.5),
width: 140,
),
const Gap(15),
const Text(
'Home automation saves your time and effort by automating routine tasks.',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
const Gap(20),
const DefaultTextButton(
text: 'Create Scene',
)
],
),
),
);
}
}
@@ -1,17 +1,16 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:syncrow_app/features/home/widgets/syncrow_logo.dart';
import 'package:syncrow_app/resource_manager/color_manager.dart';
import '../home_provider.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/home/bloc/home_cubit.dart';
import 'package:syncrow_app/features/shared_widgets/syncrow_logo.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
const DefaultAppBar({super.key});
@override
Widget build(BuildContext context) {
return Consumer<HomeProvider>(
builder: (context, provider, child) {
return BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
return AppBar(
title: const SyncrowLogo(),
actions: <Widget>[
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../home/home_provider.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/home/bloc/home_cubit.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DefaultNavBar extends StatelessWidget {
const DefaultNavBar({
@@ -10,15 +10,19 @@ class DefaultNavBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<HomeProvider>(
builder: (context, provider, child) {
return BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
return BottomNavigationBar(
onTap: (int index) => provider.updatePageIndex(index, context),
currentIndex: HomeProvider.pageIndex,
selectedItemColor: Colors.black,
onTap: (int index) =>
HomeCubit.get(context).updatePageIndex(index, context),
currentIndex: HomeCubit.pageIndex,
selectedItemColor: ColorsManager.primaryColor,
selectedLabelStyle: const TextStyle(
color: ColorsManager.primaryColor,
),
unselectedItemColor: Colors.grey,
elevation: 10,
items: provider.bottomNavItems,
items: HomeCubit.get(context).bottomNavItems,
);
},
);
@@ -1,25 +1,25 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DefaultTextButton extends StatelessWidget {
const DefaultTextButton({
super.key,
this.onPressed,
required this.text,
this.isPrimary = false,
this.isSecondary = false,
});
final void Function()? onPressed;
final String text;
final bool isPrimary;
final bool isSecondary;
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onPressed,
style: isPrimary
? ButtonStyle(
style: isSecondary
? null
: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(ColorsManager.primaryColor),
shape: MaterialStateProperty.all(
@@ -27,11 +27,10 @@ class DefaultTextButton extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
),
)
: null,
),
child: Text(
text,
style: TextStyle(color: isPrimary ? Colors.white : Colors.black),
style: TextStyle(color: isSecondary ? Colors.black : Colors.white),
),
);
}
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/resource_manager/assets_manager.dart';
import 'package:syncrow_app/utils/resource_manager/assets_manager.dart';
class SyncrowLogo extends StatelessWidget {
const SyncrowLogo({
+8
View File
@@ -0,0 +1,8 @@
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'smart_state.dart';
class SmartCubit extends Cubit<SmartState> {
SmartCubit() : super(SmartInitial());
}
+6
View File
@@ -0,0 +1,6 @@
part of 'smart_cubit.dart';
@immutable
abstract class SmartState {}
class SmartInitial extends SmartState {}
-7
View File
@@ -1,7 +0,0 @@
import 'package:flutter/material.dart';
import 'smart_state.dart';
class SmartProvider extends ChangeNotifier {
final state = SmartState();
}
-6
View File
@@ -1,6 +0,0 @@
class SmartState {
SmartState() {
// init some variables
}
}
-18
View File
@@ -1,18 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'smart_provider.dart';
class SmartPage extends StatelessWidget {
const SmartPage({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => SmartProvider(),
builder: (context, child) => const Center(
child: Text('Smart Page'),
),
);
}
}
+20
View File
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/smart/bloc/smart_cubit.dart';
class SmartPage extends StatelessWidget {
const SmartPage({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SmartCubit(),
child: BlocBuilder<SmartCubit, SmartState>(
builder: (context, state) {
return const Center(
child: Text('Smart Page'),
);
},
));
}
}
@@ -1,30 +1,22 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/splash/user_agreement_dialog.dart';
import 'package:syncrow_app/navigation/route_manager.dart';
import 'package:syncrow_app/features/splash/view/widgets/user_agreement_dialog.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/resource_manager/assets_manager.dart';
import 'package:syncrow_app/utils/resource_manager/assets_manager.dart';
class SplashView extends StatelessWidget {
const SplashView({super.key});
@override
Widget build(BuildContext context) {
//TODO Remove delay
// Future.delayed(const Duration(seconds: 5), () {
// RouteManager().routerManagerPushUntil(
// context,
// routeName: Routes.homeRoute,
// );
// });
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
RouteManager().routerManagerPopAndPushNamed(
routeName: Routes.authRoute,
context: context,
Navigator.popAndPushNamed(
context,
Routes.authRoute,
);
},
),
@@ -19,11 +19,11 @@ class UserAgreementDialog extends StatelessWidget {
onPressed: () {
Navigator.of(context).pop();
},
isPrimary: true,
),
DefaultTextButton(
text: 'I Disagree',
onPressed: () => Navigator.of(context).pop(),
isSecondary: true,
),
],
);