first commit

This commit is contained in:
kicap
2024-08-03 12:54:13 +08:00
commit 053ab5a6ac
40 changed files with 2936 additions and 0 deletions

View File

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../app/themes/app_text.dart';
import './splash_screen_view_model.dart';
class SplashScreenView extends StatelessWidget {
const SplashScreenView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<SplashScreenViewModel>.nonReactive(
viewModelBuilder: () => SplashScreenViewModel(),
onViewModelReady: (SplashScreenViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
SplashScreenViewModel model,
Widget? child,
) {
return Scaffold(
// backgroundColor: mainColor,
body: Column(
children: [
const SizedBox(),
Expanded(
child: Center(
// show the logo.png
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Image(
image: AssetImage("assets/logo.png"),
width: 200,
height: 200,
),
const SizedBox(height: 10),
Text(
"Disfungsi Traffic Light",
style: boldTextStyle.copyWith(
fontSize: 25,
),
)
],
),
),
),
Text(
"LUCKY PRASETIO AY",
textAlign: TextAlign.center,
style: boldTextStyle.copyWith(
fontSize: 15,
),
),
const SizedBox(height: 15),
const Text(
"217 280 111",
textAlign: TextAlign.center,
style: italicTextStyle,
),
const SizedBox(height: 15),
],
),
);
},
);
}
}

View File

@ -0,0 +1,15 @@
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
class SplashScreenViewModel extends CustomBaseViewModel {
final log = getLogger('SplashScreenViewModel');
Future<void> init() async {
await Future.delayed(const Duration(seconds: 2));
// navigate to login page
// ignore: use_build_context_synchronously
socketIoClient.init();
navigationService.replaceWith(Routes.theIndexView);
}
}

View File

@ -0,0 +1,277 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import './profil_view_model.dart';
class ProfilView extends StatelessWidget {
const ProfilView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<ProfilViewModel>.reactive(
viewModelBuilder: () => ProfilViewModel(),
onViewModelReady: (ProfilViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
ProfilViewModel model,
Widget? child,
) {
return Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.2,
),
const Image(
image: AssetImage("assets/logo.png"),
width: 100,
height: 100,
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.26,
child: const Text(
'Nama',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
child: const Text(" : "),
),
const SizedBox(
width: 10,
),
Flexible(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Text(
'LUCKY PRASETIO AY',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.26,
child: const Text(
'NIM',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
child: const Text(" : "),
),
const SizedBox(
width: 10,
),
Flexible(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Text(
'217 280 111',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.26,
child: const Text(
'Pembimbing 1',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
child: const Text(" : "),
),
const SizedBox(
width: 10,
),
Flexible(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Text(
'Muhammad Basri, ST., MT.',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.26,
child: const Text(
'Pembimbing 2',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
child: const Text(" : "),
),
const SizedBox(
width: 10,
),
Flexible(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Text(
'Ir. Untung Suwardoyo, S.Kom.,MT.IPP',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.26,
child: const Text(
'Judul',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
child: const Text(" : "),
),
const SizedBox(
width: 10,
),
Flexible(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Text(
'PROTOTYPE SISTEM MONITORING DISFUNGSI TRAFFIC LIGHT',
textAlign: TextAlign.justify,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.02,
),
],
)
],
);
},
);
}
}

View File

@ -0,0 +1,5 @@
import 'package:traffic_light/app/core/custom_base_view_model.dart';
class ProfilViewModel extends CustomBaseViewModel {
Future<void> init() async {}
}

View File

@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:traffic_light/app/themes/app_text.dart';
import './status_lampu_view_model.dart';
class StatusLampuView extends StatelessWidget {
const StatusLampuView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<StatusLampuViewModel>.reactive(
viewModelBuilder: () => StatusLampuViewModel(),
onViewModelReady: (StatusLampuViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
StatusLampuViewModel model,
Widget? child,
) {
// create traffic light using column
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(20),
width: 150,
height: 400,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: model.light == 'Red'
? const Color.fromARGB(255, 249, 8, 8)
: Colors.red[100],
shape: BoxShape.circle,
),
),
const SizedBox(height: 20),
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: model.light == 'Yellow'
? const Color.fromARGB(255, 245, 220, 0)
: Colors.yellow[100],
shape: BoxShape.circle,
),
),
const SizedBox(height: 20),
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: model.light == 'Green'
? const Color.fromARGB(255, 0, 255, 13)
: Colors.green[100],
shape: BoxShape.circle,
),
),
],
),
),
const SizedBox(height: 20),
Text(
model.no == null
? '...'
: model.strLength <= 2
? model.no!
: "...",
style: italicTextStyle.copyWith(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
const SizedBox(height: 20),
Text(
'Current RMS (A) : ${model.rms ?? '...'}',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
const SizedBox(height: 20),
Text(
'Status Traffic Light : ${model.plnStatus}',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
],
),
);
},
);
}
}

View File

@ -0,0 +1,35 @@
import 'package:traffic_light/app/core/custom_base_view_model.dart';
import '../../../../app/app.logger.dart';
class StatusLampuViewModel extends CustomBaseViewModel {
final log = getLogger('StatusLampuViewModel');
String plnStatus = "....";
int strLength = 0;
Future<void> init() async {
socketIoClient.on('datanya', (data) {
// log.d(data);
String rmsnya = data['rms'];
no = data['no'];
strLength = no!.length;
light = data['light'];
pln = data['pln'];
rms = double.parse(rmsnya);
log.d(rms);
if (pln == "") {
plnStatus = "....";
} else if (pln == "PLN OFF") {
plnStatus = "Listrik mati";
} else if (pln == "PLN ON" && rms! < 0.262) {
// 0.262 , 0.37
plnStatus = "Lampu terputus & rusak";
} else if (pln == "PLN ON" && rms! >= 0.262) {
plnStatus = "Lampu normal";
}
notifyListeners();
});
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../../app/app.router.dart';
import '../../../app/themes/app_colors.dart';
import '../../../app/themes/app_text.dart';
import './the_index_view_model.dart';
class TheIndexView extends StatelessWidget {
const TheIndexView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<TheIndexViewModel>.reactive(
viewModelBuilder: () => TheIndexViewModel(),
onViewModelReady: (TheIndexViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
TheIndexViewModel model,
Widget? child,
) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
appBar: AppBar(
title: Text(
model.header,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
),
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () {
// model.logout();
},
icon: const Icon(Icons.logout, color: Colors.white),
),
],
),
body: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(3),
router: TheIndexViewRouter(),
initialRoute: TheIndexViewRoutes.statusLampuView,
),
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: backgroundColor),
title: Text(
item['name'],
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: mainGrey,
),
),
backgroundColor: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? fontColor
: mainGrey,
),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizontal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
),
),
);
},
);
}
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:traffic_light/app/app.router.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
class TheIndexViewModel extends IndexTrackingViewModel {
final log = getLogger('TheIndexViewModel');
final _navigationService = locator<NavigationService>();
final _bottomNavBarList = [
{
'name': 'Status Traffic Light',
'icon': Icons.list_alt_outlined,
'header': 'Status Traffic Light',
},
{
'name': 'Profil Peneliti',
'icon': Icons.person_outline,
'header': 'Profil Peneliti',
},
];
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _views = [
TheIndexViewRoutes.statusLampuView,
TheIndexViewRoutes.profilView,
];
String header = 'Status Lampu';
Future<void> init() async {}
void handleNavigation(int index) {
log.d("handleNavigation: $index");
log.d("currentIndex: $currentIndex");
if (currentIndex == index) return;
setIndex(index);
header = _bottomNavBarList[index]['header'] as String;
_navigationService.navigateTo(
_views[index],
id: 3,
);
}
}

View File

@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import '../../app/themes/app_colors.dart';
class MyButton extends StatelessWidget {
const MyButton({
Key? key,
required this.text,
this.onPressed,
this.color = mainColor,
}) : super(key: key);
final String text;
final VoidCallback? onPressed;
final Color color;
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: color,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
onPressed: onPressed,
child: Text(
text,
style: const TextStyle(
color: backgroundColor,
fontSize: 18,
),
),
);
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import '../../app/themes/app_colors.dart';
class MyTextFormField extends StatelessWidget {
const MyTextFormField({
Key? key,
this.labelText,
this.hintText,
this.obscureText,
this.validator,
this.suffixIcon,
this.prefixIcon,
this.focusNode,
this.controller,
this.maxLines = 1,
this.onEditingComplete,
this.readOnly = false,
this.onTap,
this.keyboardType = TextInputType.text,
this.initialValue,
this.enabled = true,
this.maxLength,
this.width = double.infinity,
}) : super(key: key);
final String? labelText;
final String? hintText;
final bool? obscureText;
final FormFieldValidator<String>? validator;
final Widget? suffixIcon;
final Widget? prefixIcon;
final FocusNode? focusNode;
final TextEditingController? controller;
final int maxLines;
final VoidCallback? onEditingComplete;
final bool readOnly;
final VoidCallback? onTap;
final TextInputType keyboardType;
final String? initialValue;
final bool enabled;
final int? maxLength;
final double? width;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: TextFormField(
maxLength: maxLength,
enabled: enabled,
initialValue: initialValue,
onEditingComplete: onEditingComplete,
maxLines: maxLines,
controller: controller,
focusNode: focusNode,
obscureText: obscureText ?? false,
readOnly: readOnly,
onTap: onTap,
keyboardType: keyboardType,
decoration: InputDecoration(
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,
enabledBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
color: mainColor,
),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
color: mainColor,
),
),
focusedErrorBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
color: dangerColor,
),
),
errorBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
color: dangerColor,
),
),
labelText: labelText,
hintText: hintText,
labelStyle: const TextStyle(color: fontColor),
),
validator: validator,
),
);
}
}