repair add makanan page, addes socket io client for real time update data, added table status change page

This commit is contained in:
kicap
2023-08-25 04:22:48 +08:00
parent 24cc3d3bd7
commit b3781eb831
33 changed files with 1233 additions and 349 deletions

12
.env
View File

@ -2,6 +2,12 @@
# api_url = 'https://panti-asuhan.s-keytech.com/api/'
# # url = 'http://172.29.85.181/panti_asuhan2/'
# # api_url = 'http://172.29.85.181/panti_asuhan2/api/'
url = 'http://20.20.20.25:3001/'
table_url = 'http://20.20.20.25:3001/table'
api_url = 'http://20.20.20.25:3001/table'
# url = 'http://20.20.20.25:3001/'
# table_url = 'http://20.20.20.25:3001/table'
# api_url = 'http://20.20.20.25:3001/table'
# url = 'http://192.168.20.232:3001/'
# table_url = 'http://192.168.20.232:3001/table'
# api_url = 'http://192.168.20.232:3001/table'
url = 'https://reza_backend.kicap-karan.com/'
table_url = 'https://reza_backend.kicap-karan.com/table'
api_url = 'https://reza_backend.kicap-karan.com/table'

View File

@ -1,10 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="reza_admin"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"
>
<activity
android:name=".MainActivity"
android:exported="true"

BIN
assets/nasi_goreng.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View File

@ -1,9 +1,11 @@
import 'package:reza_admin/ui/views/admin_ui/makanan_list/add_edit_makanan/add_edit_makanan_text_form_dialog/add_edit_makanan_text_form_dialog_view.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stacked/stacked_annotations.dart';
import '../services/global_var.dart';
import '../services/http_services.dart';
import '../services/my_easyloading.dart';
import '../services/my_socket_io_client.dart';
import '../services/other_function.dart';
import '../ui/views/admin_ui/admin_index_tracking/admin_index_tracking_view.dart';
import '../ui/views/admin_ui/akun_admin/akun_admin_view.dart';
@ -12,6 +14,7 @@ import '../ui/views/admin_ui/makanan_list/detail_makanan/detail_makanan_view.dar
import '../ui/views/admin_ui/makanan_list/makanan_list_view.dart';
import '../ui/views/admin_ui/meja_list/meja_detail/meja_detail_view.dart';
import '../ui/views/admin_ui/meja_list/meja_edit/meja_edit_view.dart';
import '../ui/views/admin_ui/meja_list/meja_edit_status_dialog/meja_edit_status_dialog_view.dart';
import '../ui/views/admin_ui/meja_list/meja_history_log/meja_history_log_view.dart';
import '../ui/views/admin_ui/meja_list/meja_list_view.dart';
import '../ui/views/admin_ui/pesanan_list/pesanan_list_view.dart';
@ -38,9 +41,10 @@ import '../ui/views/splash_screen/splash_screen_view.dart';
MaterialRoute(page: MejaEditView),
MaterialRoute(page: MejaHistoryLogView),
],
// dialogs: [
// StackedDialog(classType: AddSiswaDialogView),
// ],
dialogs: [
StackedDialog(classType: MejaEditStatusDialogView),
StackedDialog(classType: AddEditMakananTextFormDialogView),
],
dependencies: [
LazySingleton(classType: NavigationService),
LazySingleton(classType: DialogService),
@ -52,6 +56,7 @@ import '../ui/views/splash_screen/splash_screen_view.dart';
LazySingleton(classType: MyHttpServices),
LazySingleton(classType: OtherFunction),
LazySingleton(classType: GlobalVar),
LazySingleton(classType: MySocketIoClient),
],
logger: StackedLogger(),
)

30
lib/app/app.dialogs.dart Normal file
View File

@ -0,0 +1,30 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedDialogGenerator
// **************************************************************************
import 'package:stacked_services/stacked_services.dart';
import 'app.locator.dart';
import '../ui/views/admin_ui/makanan_list/add_edit_makanan/add_edit_makanan_text_form_dialog/add_edit_makanan_text_form_dialog_view.dart';
import '../ui/views/admin_ui/meja_list/meja_edit_status_dialog/meja_edit_status_dialog_view.dart';
enum DialogType {
mejaEditStatusDialogView,
addEditMakananTextFormDialogView,
}
void setupDialogUi() {
final dialogService = locator<DialogService>();
final Map<DialogType, DialogBuilder> builders = {
DialogType.mejaEditStatusDialogView: (context, request, completer) =>
MejaEditStatusDialogView(request: request, completer: completer),
DialogType.addEditMakananTextFormDialogView:
(context, request, completer) => AddEditMakananTextFormDialogView(
request: request, completer: completer),
};
dialogService.registerCustomDialogBuilders(builders);
}

View File

@ -15,6 +15,7 @@ import 'package:stacked_shared/stacked_shared.dart';
import '../services/global_var.dart';
import '../services/http_services.dart';
import '../services/my_easyloading.dart';
import '../services/my_socket_io_client.dart';
import '../services/other_function.dart';
final locator = StackedLocator.instance;
@ -36,4 +37,5 @@ Future<void> setupLocator({
locator.registerLazySingleton(() => MyHttpServices());
locator.registerLazySingleton(() => OtherFunction());
locator.registerLazySingleton(() => GlobalVar());
locator.registerLazySingleton(() => MySocketIoClient());
}

View File

@ -1,14 +1,18 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:stacked_services/stacked_services.dart';
import 'app/app.dialogs.dart';
import 'app/app.locator.dart';
import 'app/app.router.dart';
import 'app/themes/app_theme.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
HttpOverrides.global = MyHttpOverrides();
await dotenv.load(fileName: ".env");
await setupAllLocator();
runApp(const MyApp());
@ -33,7 +37,16 @@ class MyApp extends StatelessWidget {
Future<void> setupAllLocator() async {
await setupLocator();
// setupDialogUi();
setupDialogUi();
// setupBottomsheetUi();
// setupSnackbarUi();
}
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
}

View File

@ -0,0 +1,32 @@
class MakananModel {
int? idMakanan;
String? namaMakanan;
String? hargaMakanan;
String? deskripsiMakanan;
String? imgUrl;
MakananModel(
{this.idMakanan,
this.namaMakanan,
this.hargaMakanan,
this.deskripsiMakanan,
this.imgUrl});
MakananModel.fromJson(Map<String, dynamic> json) {
idMakanan = json['id_makanan'];
namaMakanan = json['nama_makanan'];
hargaMakanan = json['harga_makanan'];
deskripsiMakanan = json['deskripsi_makanan'];
imgUrl = json['img_url'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id_makanan'] = idMakanan;
data['nama_makanan'] = namaMakanan;
data['harga_makanan'] = hargaMakanan;
data['deskripsi_makanan'] = deskripsiMakanan;
data['img_url'] = imgUrl;
return data;
}
}

21
lib/model/my_model.dart Normal file
View File

@ -0,0 +1,21 @@
class MyModel {
String? message;
bool? theBool;
dynamic data;
MyModel({this.message, this.theBool, this.data});
MyModel.fromJson(Map<String, dynamic> json) {
message = json['message'];
theBool = json['bool'];
data = json['data'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['message'] = message;
data['bool'] = theBool;
data['data'] = this.data;
return data;
}
}

View File

@ -0,0 +1,36 @@
class ReservasiMejaModel {
int? idMeja;
int? idUser;
String? status;
String? jamBooking;
String? createdAt;
String? updatedAt;
ReservasiMejaModel(
{this.idMeja,
this.idUser,
this.status,
this.jamBooking,
this.createdAt,
this.updatedAt});
ReservasiMejaModel.fromJson(Map<String, dynamic> json) {
idMeja = json['id_meja'];
idUser = json['id_user'];
status = json['status'];
jamBooking = json['jam_booking'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id_meja'] = idMeja;
data['id_user'] = idUser;
data['status'] = status;
data['jam_booking'] = jamBooking;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
return data;
}
}

44
lib/model/user_model.dart Normal file
View File

@ -0,0 +1,44 @@
class UserModel {
int? idUser;
String? nama;
String? noTelpon;
String? tanggalLahir;
String? jenisKelamin;
String? alamat;
String? createdAt;
String? updatedAt;
UserModel(
{this.idUser,
this.nama,
this.noTelpon,
this.tanggalLahir,
this.jenisKelamin,
this.alamat,
this.createdAt,
this.updatedAt});
UserModel.fromJson(Map<String, dynamic> json) {
idUser = json['id_user'];
nama = json['nama'];
noTelpon = json['no_telpon'];
tanggalLahir = json['tanggal_lahir'];
jenisKelamin = json['jenis_kelamin'];
alamat = json['alamat'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id_user'] = idUser;
data['nama'] = nama;
data['no_telpon'] = noTelpon;
data['tanggal_lahir'] = tanggalLahir;
data['jenis_kelamin'] = jenisKelamin;
data['alamat'] = alamat;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
return data;
}
}

View File

@ -6,7 +6,7 @@ import '../app/app.logger.dart';
class MyHttpServices {
final log = getLogger('MyHttpServices');
final _options = BaseOptions(
baseUrl: dotenv.env['api_url']!,
baseUrl: dotenv.env['url']!,
connectTimeout: const Duration(seconds: 120),
receiveTimeout: const Duration(seconds: 120),
);
@ -20,14 +20,22 @@ class MyHttpServices {
Future<Response> get(String path) async {
try {
return await _dio.get(path);
} on DioException {
} on DioException catch (e) {
log.e('error : $e');
rethrow;
}
}
Future<Response> postWithFormData(String path, FormData formData) async {
try {
return await _dio.post(path, data: formData);
return await _dio.post(
path,
data: formData,
// method="POST" enctype="multipart/form-data">
options: Options(
contentType: 'multipart/form-data',
),
);
} on DioException {
rethrow;
}

View File

@ -9,7 +9,7 @@ class MyEasyLoading {
);
}
dismissLoading() {
dismiss() {
EasyLoading.dismiss();
}

View File

@ -0,0 +1,48 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:socket_io_client/socket_io_client.dart';
import '../app/app.logger.dart';
class MySocketIoClient {
final log = getLogger('MySocketIoClient');
final String _url = dotenv.env['url']!;
static final MySocketIoClient _instance = MySocketIoClient._internal();
factory MySocketIoClient() => _instance;
MySocketIoClient._internal();
late Socket _socket;
Socket get socket => _socket;
Future<void> init() async {
try {
_socket = io(_url, <String, dynamic>{
'transports': ['websocket'],
'autoConnect': false,
});
_socket.connect();
log.i('socket connected');
} catch (e) {
log.e('error : $e');
}
}
Future<void> emit(String event, dynamic data) async {
_socket.emit(event, data);
}
Future<void> on(String event, Function(dynamic) callback) async {
_socket.on(event, callback);
}
Future<void> off(String event) async {
_socket.off(event);
}
Future<void> disconnect() async {
_socket.disconnect();
}
Future<void> connect() async {
_socket.connect();
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:reza_admin/app/themes/app_text.dart';
import 'package:reza_admin/ui/widgets/my_textformfield.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import './add_edit_makanan_text_form_dialog_view_model.dart';
class AddEditMakananTextFormDialogView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
const AddEditMakananTextFormDialogView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<AddEditMakananTextFormDialogViewModel>.reactive(
viewModelBuilder: () => AddEditMakananTextFormDialogViewModel(),
onViewModelReady: (AddEditMakananTextFormDialogViewModel model) async {
await model.init(request!.data);
},
builder: (
BuildContext context,
AddEditMakananTextFormDialogViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
model.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
MyTextFormField(
hintText: model.description,
maxLines: model.maxLines,
controller: model.textEditingController,
keyboardType: model.keyboardType
? TextInputType.number
: TextInputType.text,
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
completer!(DialogResponse(confirmed: false));
},
child: Text(
'Cancel',
style: boldTextStyle.copyWith(color: Colors.red),
),
),
TextButton(
onPressed: () {
if (model.textEditingController.text.isNotEmpty) {
completer!(DialogResponse(
confirmed: true,
data: model.textEditingController.text,
));
} else {
model.snackbarService.showSnackbar(
message: 'Please fill the form',
title: 'Error',
);
}
// completer!(DialogResponse(
// confirmed: true,
// responseData: model.status,
// ));
},
child: const Text('Save'),
),
],
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import '../../../../../../app/app.logger.dart';
import '../../../../../../app/core/custom_base_view_model.dart';
class AddEditMakananTextFormDialogViewModel extends CustomBaseViewModel {
final log = getLogger('AddEditMakananTextFormDialogViewModel');
late String title;
late String description;
late int maxLines;
late bool keyboardType;
TextEditingController textEditingController = TextEditingController();
Future<void> init(data) async {
log.i(data);
title = data['title'];
description = data['description'];
maxLines = data['maxLines'];
keyboardType = data['keyboardType'];
}
}

View File

@ -35,159 +35,187 @@ class AddEditMakananView extends HookWidget {
return true;
},
child: Scaffold(
backgroundColor: backgroundColor,
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
ListView.builder(
controller: scrollController,
itemCount: 1,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).padding.top,
),
const TopMenuWidget(),
const SecondWidget(),
const SizedBox(
height: 10,
),
MyWhiteContainer(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Ongkos Kirim',
style: boldTextStyle.copyWith(
fontSize: 14,
),
backgroundColor: backgroundColor,
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
ListView.builder(
controller: scrollController,
itemCount: 1,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).padding.top,
),
const TopMenuWidget(),
const SecondWidget(),
const SizedBox(
height: 10,
),
MyWhiteContainer(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Ongkos Kirim',
style: boldTextStyle.copyWith(
fontSize: 14,
),
TextSpan(
text: ' Harga Ongkir Di Sini',
style: boldTextStyle.copyWith(
fontSize: 14,
color: dangerColor,
fontStyle: FontStyle.italic,
),
),
TextSpan(
text: ' Rp. 10.000',
style: boldTextStyle.copyWith(
fontSize: 14,
color: dangerColor,
fontStyle: FontStyle.italic,
),
],
),
),
],
),
),
const SizedBox(
width: 10,
),
IconButton(
onPressed: () {},
icon: const Icon(
Icons.edit,
color: dangerColor,
),
iconSize: 20,
),
const SizedBox(
width: 10,
),
],
),
const SizedBox(
height: 10,
),
Text(
"Bisa Dibayar COD sekitar Parepare",
style: regularTextStyle.copyWith(
fontSize: 13,
color: fontGrey,
),
const SizedBox(
width: 10,
),
// IconButton(
// onPressed: () async {
// String? data =
// await model.addEditDialog(
// title: 'Ongkos Kirim',
// description: 'Harga Ongkir Di Sini',
// keyboardType: true,
// maxLines: 1,
// );
// // model.log.i('data: $data');
// model.hargaOngkir = data;
// model.notifyListeners();
// },
// icon: const Icon(
// Icons.edit,
// color: dangerColor,
// ),
// iconSize: 20,
// ),
const SizedBox(
width: 10,
),
],
),
const SizedBox(
height: 10,
),
Text(
"Bisa Dibayar COD sekitar Parepare",
style: regularTextStyle.copyWith(
fontSize: 13,
color: fontGrey,
),
],
),
),
],
),
),
const SizedBox(
height: 10,
),
MyWhiteContainer(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"Deskripsi",
style: boldTextStyle.copyWith(
fontSize: 15,
),
),
const SizedBox(
height: 10,
),
MyWhiteContainer(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"Deskripsi",
style: boldTextStyle.copyWith(
fontSize: 15,
),
),
const SizedBox(
width: 10,
),
IconButton(
onPressed: () {},
icon: const Icon(
Icons.edit,
color: mainGrey,
),
iconSize: 20,
),
const SizedBox(
width: 10,
),
],
),
const SizedBox(
height: 10,
),
// bikin dummy text tentang nasi goreng
Text(
"Deskripsi Makanan Di Sini",
style: regularTextStyle.copyWith(
fontSize: 13,
color: fontGrey,
fontStyle: FontStyle.italic,
),
textAlign: TextAlign.justify,
const SizedBox(
width: 10,
),
IconButton(
onPressed: () async {
String? data =
await model.addEditDialog(
title: 'Deskripsi',
description:
'Deskripsi Makanan Di Sini',
maxLines: 5);
// model.log.i('data: $data');
model.deskripsi = data;
model.notifyListeners();
},
icon: const Icon(
Icons.edit,
color: mainGrey,
),
iconSize: 20,
),
const SizedBox(
width: 10,
),
],
),
const SizedBox(
height: 10,
),
// bikin dummy text tentang nasi goreng
Text(
model.deskripsi ??
"Deskripsi Makanan Di Sini",
style: regularTextStyle.copyWith(
fontSize: 13,
color: fontGrey,
fontStyle: FontStyle.italic,
),
],
),
textAlign: TextAlign.justify,
),
],
),
),
const SizedBox(
height: 20,
),
],
);
},
),
TopBarWidget(opacity: opacity),
],
),
),
const SizedBox(
height: 20,
),
],
);
},
),
TopBarWidget(opacity: opacity),
],
),
bottomNavigationBar: Container(
),
bottomNavigationBar: GestureDetector(
onTap: () {
model.tambahEditMakanan();
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
@ -220,7 +248,9 @@ class AddEditMakananView extends HookWidget {
),
),
),
)),
),
),
),
);
},
);
@ -351,7 +381,7 @@ class SecondWidget extends ViewModelWidget<AddEditMakananViewModel> {
children: [
Expanded(
child: Text(
"Nama Makanan Di Sini",
viewModel.namaMakanan ?? 'Nama Makanan Di Sini',
style: regularTextStyle.copyWith(
fontSize: 17,
fontStyle: FontStyle.italic,
@ -362,7 +392,16 @@ class SecondWidget extends ViewModelWidget<AddEditMakananViewModel> {
width: 10,
),
IconButton(
onPressed: () {},
onPressed: () async {
String? data = await viewModel.addEditDialog(
title: 'Nama Makanan',
description: 'Nama Makanan Di Sini',
);
// viewModel.log.i('data: $data');
viewModel.namaMakanan = data;
viewModel.notifyListeners();
},
icon: const Icon(
Icons.edit,
color: mainGrey,
@ -392,7 +431,18 @@ class SecondWidget extends ViewModelWidget<AddEditMakananViewModel> {
width: 10,
),
IconButton(
onPressed: () {},
onPressed: () async {
String? data = await viewModel.addEditDialog(
title: 'Harga Makanan',
description: 'Harga Makanan Di Sini',
keyboardType: true,
maxLines: 1,
);
// viewModel.log.i('data: $data');
viewModel.hargaMakanan = data;
viewModel.notifyListeners();
},
icon: const Icon(
Icons.edit,
color: dangerColor,
@ -432,14 +482,19 @@ class TopMenuWidget extends ViewModelWidget<AddEditMakananViewModel> {
// 'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
// fit: BoxFit.cover,
// ),
child: Center(
child: Text(
'Add Image',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
),
child: viewModel.imageBytes != null
? Image.memory(
viewModel.imageBytes!,
fit: BoxFit.cover,
)
: Center(
child: Text(
'Add Image',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
),
),
// Positioned(
// bottom: 10,
@ -468,21 +523,24 @@ class TopMenuWidget extends ViewModelWidget<AddEditMakananViewModel> {
Positioned(
bottom: 20,
right: 20,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 5,
),
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(10),
),
child: const Center(
child: Icon(
Icons.camera_alt_outlined,
color: Colors.white,
size: 30,
child: GestureDetector(
onTap: () => viewModel.addImage(),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 5,
),
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(10),
),
child: const Center(
child: Icon(
Icons.camera_alt_outlined,
color: Colors.white,
size: 30,
),
),
),
),

View File

@ -1,9 +1,105 @@
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart';
import '../../../../../app/app.dialogs.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
class AddEditMakananViewModel extends CustomBaseViewModel {
final log = getLogger('AddMakananViewModel');
String? namaMakanan;
String? hargaMakanan;
// String? hargaOngkir;
String? deskripsi;
String? _imagePath;
final ImagePicker _picker = ImagePicker();
XFile? imageFile;
Uint8List? imageBytes;
Future<void> init() async {
globalVar.backPressed = 'backNormal';
}
void addImage() async {
try {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
imageFile = image;
_imagePath = image.path;
imageBytes = await image.readAsBytes();
log.i('image path: $_imagePath');
notifyListeners();
}
} catch (e) {
log.e(e);
}
}
Future<String?> addEditDialog(
{required String title,
required String description,
int maxLines = 2,
bool keyboardType = false}) async {
var res = await dialogService.showCustomDialog(
variant: DialogType.addEditMakananTextFormDialogView,
data: {
'title': title,
'description': description,
'maxLines': maxLines,
'keyboardType': keyboardType,
},
);
if (res!.confirmed) {
return res.data;
} else {
return null;
}
}
tambahEditMakanan() async {
if (namaMakanan == null ||
hargaMakanan == null ||
// hargaOngkir == null ||
deskripsi == null ||
imageFile == null) {
await dialogService.showDialog(
title: 'Error',
description: 'Semua field harus diisi',
);
} else {
easyLoading.customLoading('Tambah Makanan');
setBusy(true);
try {
var formData = FormData.fromMap({
'nama_makanan': namaMakanan,
'harga_makanan': hargaMakanan,
// 'harga_ongkir': hargaOngkir,
'deskripsi_makanan': deskripsi,
'image': await MultipartFile.fromFile(_imagePath!),
});
var res = await httpService.postWithFormData('table/makanan', formData);
log.i(res.data);
setBusy(false);
easyLoading.dismiss();
dialogService.showDialog(
title: 'Berhasil',
description: 'Makanan berhasil ditambahkan',
);
globalVar.backPressed = 'exitApp';
navigationService.back();
navigationService.back();
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismiss();
}
}
}
}

View File

@ -371,8 +371,8 @@ class TopMenuWidget extends ViewModelWidget<DetailMakananViewModel> {
SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.35,
child: Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
child: Image.asset(
'assets/nasi_goreng.jpg',
fit: BoxFit.cover,
),
),

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:reza_admin/app/app.router.dart';
import 'package:stacked/stacked.dart';
@ -141,66 +142,94 @@ class MakananListView extends StatelessWidget {
height: 10,
),
Expanded(
child: Center(
child: SingleChildScrollView(
child: Wrap(
spacing: 10,
runSpacing: 10,
children: [
for (var i = 0; i < 10; i++)
GestureDetector(
onTap: () => model.goToDetailMakanan(),
child: Container(
width: MediaQuery.of(context).size.width * 0.46,
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
height: 150,
width: double.infinity,
fit: BoxFit.fill,
),
const SizedBox(
height: 5,
),
const Padding(
padding: EdgeInsets.only(
left: 5,
),
child: Text(
'Product Name',
style: TextStyle(
fontWeight: FontWeight.bold,
child: model.isBusy
? const Center(
child: CircularProgressIndicator(),
)
: (model.listMakanan.isEmpty
? const Center(
child: Text('Data Kosong'),
)
: SingleChildScrollView(
child: Wrap(
spacing: 5,
runSpacing: 10,
// alignment: WrapAlignment.spaceAround,
// crossAxisAlignment: WrapCrossAlignment.center,
children: [
for (var i = 0;
i < model.listMakanan.length;
i++)
GestureDetector(
onTap: () => model.goToDetailMakanan(),
child: Padding(
padding: const EdgeInsets.only(
left: 10,
// right: 5,
),
child: Container(
width: MediaQuery.of(context)
.size
.width *
0.46,
color: Colors.white,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Image.network(
'${dotenv.env['url']}assets/makanan/${model.listMakanan[i].imgUrl}',
height: 150,
width: double.infinity,
fit: BoxFit.fill,
),
// Image.asset(
// 'assets/nasi_goreng.jpg',
// height: 150,
// width: double.infinity,
// fit: BoxFit.fill,
// ),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.only(
left: 5,
),
child: Text(
model.listMakanan[i]
.namaMakanan!,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.only(
left: 5,
),
child: Text(
model.listMakanan[i]
.deskripsiMakanan!,
style: const TextStyle(
color: Colors.grey,
),
),
),
const SizedBox(
height: 5,
),
],
),
),
),
),
const SizedBox(
height: 5,
),
const Padding(
padding: EdgeInsets.only(
left: 5,
),
child: Text(
'Rp. 100.000',
style: TextStyle(
color: Colors.grey,
),
),
),
const SizedBox(
height: 5,
),
],
),
],
),
),
],
),
),
),
)),
),
],
),

View File

@ -1,11 +1,46 @@
import 'package:reza_admin/model/makanan_model.dart';
import 'package:reza_admin/model/my_model.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/app.router.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../services/my_socket_io_client.dart';
class MakananListViewModel extends CustomBaseViewModel {
final log = getLogger('MakananListViewModel');
final socketIoClient = locator<MySocketIoClient>();
List<MakananModel> listMakanan = [];
Future<void> init() async {
globalVar.backPressed = 'exitApp';
getData();
socketIoClient.on('makanan_user', (data) {
log.i('data : $data');
listMakanan.clear();
getData();
// webViewController!.reload();
});
}
getData() async {
setBusy(true);
easyLoading.showLoading();
try {
var res = await httpService.get('table/makanan');
MyModel myModel = MyModel.fromJson(res.data);
if (myModel.data.length > 0) {
for (var item in myModel.data) {
listMakanan.add(MakananModel.fromJson(item));
}
}
log.i(listMakanan);
} catch (e) {
log.e(e.toString());
} finally {
easyLoading.dismiss();
setBusy(false);
}
}
goToDetailMakanan() {

View File

@ -87,9 +87,9 @@ class MejaDetailView extends StatelessWidget {
style: regularTextStyle,
children: [
TextSpan(
text: 'Tersedia',
text: model.theBool ? 'Tidak Tersedia' : 'Tersedia',
style: regularTextStyle.copyWith(
color: Colors.green,
color: model.theBool ? Colors.red : Colors.green,
fontWeight: FontWeight.bold,
),
),
@ -145,58 +145,60 @@ class MejaDetailView extends StatelessWidget {
),
),
const SizedBox(height: 10),
Expanded(
child: ListView.builder(
itemCount: 2,
shrinkWrap: true,
itemBuilder: (context, index) {
// make the color random between red and blue
const color = Colors.blue;
String pesanStatus = 'Menunggu Pengesahan';
return Card(
color: color,
child: ListTile(
// leading: CircleAvatar(
// backgroundColor: Colors.white,
// child: Text(
// index.toString(),
// style: regularTextStyle,
// ),
// ),
title: Text(
'Nama Pemesan',
style: regularTextStyle.copyWith(
fontSize: 18,
color: Colors.white,
fontStyle: FontStyle.italic,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'08:00.00 - 09:00.00',
style: TextStyle(
color: Colors.white,
),
),
Text(
pesanStatus,
style: const TextStyle(
color: Colors.white,
),
),
],
),
trailing: const Icon(Icons.arrow_forward_ios,
color: Colors.white),
onTap: () {
model.log.i('Meja 1');
},
),
);
},
),
const Expanded(
child: SizedBox(),
// ListView.builder(
// itemCount: 2,
// shrinkWrap: true,
// itemBuilder: (context, index) {
// // make the color random between red and blue
// const color = Colors.blue;
// String pesanStatus = 'Menunggu Pengesahan';
// return Card(
// color: color,
// child: ListTile(
// // leading: CircleAvatar(
// // backgroundColor: Colors.white,
// // child: Text(
// // index.toString(),
// // style: regularTextStyle,
// // ),
// // ),
// title: Text(
// 'Nama Pemesan',
// style: regularTextStyle.copyWith(
// fontSize: 18,
// color: Colors.white,
// fontStyle: FontStyle.italic,
// ),
// ),
// subtitle: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// const Text(
// '08:00.00 - 09:00.00',
// style: TextStyle(
// color: Colors.white,
// ),
// ),
// Text(
// pesanStatus,
// style: const TextStyle(
// color: Colors.white,
// ),
// ),
// ],
// ),
// trailing: const Icon(Icons.arrow_forward_ios,
// color: Colors.white),
// onTap: () {
// model.log.i('Meja 1');
// },
// ),
// );
// },
// ),
),
],
),
@ -262,8 +264,12 @@ class MejaDetailView extends StatelessWidget {
onTap: () {
model.log.i('List');
// model.navigationService.navigateToMakananListView();
model.navigationService
.navigateToMejaHistoryLogView(mejaId: mejaId);
// model.navigationService
// .navigateToMejaHistoryLogView(mejaId: mejaId);
model.snackbarService.showSnackbar(
message: 'Fitur ini belum tersedia',
title: 'Info',
duration: const Duration(seconds: 2));
},
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -292,6 +298,7 @@ class MejaDetailView extends StatelessWidget {
backgroundColor: orangeColor,
onPressed: () {
// model.navigationService.navigateToAddEditMakananView();
model.editStatus();
},
// create a add product button
child: const Icon(

View File

@ -1,3 +1,6 @@
import 'package:reza_admin/model/my_model.dart';
import '../../../../../app/app.dialogs.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
@ -6,16 +9,20 @@ class MejaDetailViewModel extends CustomBaseViewModel {
late String mejaId;
late String namaMeja;
late int idMeja;
String? imgAsset;
bool theBool = false;
Future<void> init(String mejaId) async {
log.i('MejaDetailViewModel init');
log.i('mejaId : $mejaId');
// log.i('MejaDetailViewModel init');
// log.i('mejaId : $mejaId');
this.mejaId = mejaId;
globalVar.backPressed = 'backNormal';
// seperate the number from the string
var number = int.parse(mejaId.replaceAll(RegExp(r'[^0-9]'), ''));
idMeja = number;
// log.i('number : $number');
if (number <= 4) {
namaMeja = 'Gazebo';
@ -30,6 +37,35 @@ class MejaDetailViewModel extends CustomBaseViewModel {
namaMeja = '$namaMeja $number';
log.i('imgAsset : $imgAsset');
// log.i('imgAsset : $imgAsset');
getData();
}
getData() async {
easyLoading.showLoading();
setBusy(true);
try {
var response = await httpService.get('table/detail/$idMeja');
// log.i('response : $response');
MyModel myModel = MyModel.fromJson(response.data);
theBool = myModel.theBool!;
log.i('theBool : $theBool');
} catch (e) {
log.e('error : $e');
} finally {
setBusy(false);
easyLoading.dismiss();
}
}
editStatus() async {
var res = await dialogService.showCustomDialog(
variant: DialogType.mejaEditStatusDialogView,
data: {'idMeja': idMeja},
);
if (res!.confirmed) {
getData();
}
}
}

View File

@ -30,6 +30,7 @@ class MejaEditView extends StatelessWidget {
'backPressed : ${model.globalVar.backPressed} in MejaEditView');
if (model.globalVar.backPressed == 'backNormal') {
// model.globalVar.backPressed = 'exitApp';
// model.globalVar.backPressed = 'backNormal';
return true;
}
// model.quitApp(context);
@ -44,7 +45,7 @@ class MejaEditView extends StatelessWidget {
leading: IconButton(
onPressed: () {
if (model.globalVar.backPressed == 'backNormal') {
model.globalVar.backPressed = 'exitApp';
// model.globalVar.backPressed = 'exitApp';
model.navigationService.back();
// return true;
}

View File

@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:reza_admin/app/themes/app_colors.dart';
import 'package:reza_admin/app/themes/app_text.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import './meja_edit_status_dialog_view_model.dart';
class MejaEditStatusDialogView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
const MejaEditStatusDialogView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<MejaEditStatusDialogViewModel>.reactive(
viewModelBuilder: () => MejaEditStatusDialogViewModel(),
onViewModelReady: (MejaEditStatusDialogViewModel model) async {
await model.init(request!.data);
},
builder: (
BuildContext context,
MejaEditStatusDialogViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Edit Status',
style: regularTextStyle,
),
const SizedBox(height: 20),
DropdownButtonFormField<String>(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Status',
),
value: model.status,
onChanged: (String? value) {
model.status = value!;
},
items: model.statusList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value, style: regularTextStyle),
);
}).toList(),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
completer!(DialogResponse(confirmed: false));
},
child: Text(
'Batal',
style: regularTextStyle.copyWith(color: redColor),
),
),
TextButton(
onPressed: () async {
bool res = await model.editStatus();
if (res) {
completer!(DialogResponse(
confirmed: true,
));
} else {
completer!(DialogResponse(
confirmed: false,
));
}
completer!(DialogResponse(
confirmed: true,
// responseData: model.status,
));
},
child: Text(
'Simpan',
style: regularTextStyle.copyWith(color: greenColor),
),
),
],
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,39 @@
import 'package:dio/dio.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
class MejaEditStatusDialogViewModel extends CustomBaseViewModel {
final log = getLogger('MejaEditStatusDialogViewModel');
int? idMeja;
String status = 'Tersedia';
List<String> statusList = ['Tersedia', 'Tidak Tersedia'];
Future<void> init(data) async {
log.i("data : ${data['idMeja']}");
idMeja = data['idMeja'];
}
Future<bool> editStatus() async {
easyLoading.showLoading();
setBusy(true);
try {
var formData = FormData.fromMap({
'id_meja': idMeja,
'status': status,
});
var response = await httpService.postWithFormData(
'table/reservation/$idMeja', formData);
log.i('response : $response');
return true;
} catch (e) {
log.e('error : $e');
return false;
} finally {
setBusy(false);
easyLoading.dismiss();
}
}
}

View File

@ -55,10 +55,7 @@ class MejaListView extends StatelessWidget {
// initialUrl: 'https://rekam-medis.airlangga-it.com/',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// _controller.complete(webViewController);
// model.controllerCompleter.future
// .then((value) => model.webViewController = value);
// model.controllerCompleter.complete(webViewController);
model.webViewController = webViewController;
},
onProgress: (int progress) {
// model.log.i('WebView is loading (progress : $progress%)');
@ -101,7 +98,7 @@ class MejaListView extends StatelessWidget {
},
onPageFinished: (String url) {
model.log.i('Page finished loading: $url');
model.easyLoading.dismissLoading();
model.easyLoading.dismiss();
},
gestureNavigationEnabled: true,
backgroundColor: const Color(0x00000000),
@ -169,64 +166,89 @@ class MejaListView extends StatelessWidget {
height: 15,
),
Expanded(
child: ListView.builder(
itemCount: 15,
shrinkWrap: true,
itemBuilder: (context, index) {
// make the color random between red and blue
final color = index % 2 == 0 ? Colors.red : Colors.blue;
String pesanStatus =
index % 2 == 0 ? 'Dibooking' : 'Menunggu Pengesahan';
return Card(
color: color,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.white,
child: Text(
index.toString(),
style: regularTextStyle,
),
),
title: Text(
'Nama Pemesan',
style: regularTextStyle.copyWith(
fontSize: 18,
color: Colors.white,
fontStyle: FontStyle.italic,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'08:00.00 - 09:00.00',
style: TextStyle(
color: Colors.white,
child: model.isBusy
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: model.reservasiMejaList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
// make the color random between red and blue
final color =
model.reservasiMejaList[index].status ==
'booking'
? Colors.red
: (model.reservasiMejaList[index].status ==
'Tidak Tersedia'
? Colors.grey[600]
: Colors.green);
String pesanStatus = model
.reservasiMejaList[index].status!
.toUpperCase();
return Card(
color: color,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.white,
child: Text(
model.reservasiMejaList[index].idMeja
.toString(),
style: regularTextStyle,
),
),
),
Text(
pesanStatus,
style: const TextStyle(
color: Colors.white,
title: Text(
model.userModelList[index] != null
? model.userModelList[index]!.nama ?? '-'
: '-',
style: regularTextStyle.copyWith(
fontSize: 18,
color: Colors.white,
fontStyle: FontStyle.italic,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
model.reservasiMejaList[index]
.jamBooking ??
'',
style: const TextStyle(
color: Colors.white,
),
),
Text(
pesanStatus,
style: const TextStyle(
color: Colors.white,
),
),
],
),
trailing: const Icon(Icons.arrow_forward_ios,
color: Colors.white),
onTap: () {
model.log.i('Meja 1');
},
),
],
),
trailing: const Icon(Icons.arrow_forward_ios,
color: Colors.white),
onTap: () {
model.log.i('Meja 1');
);
},
),
);
},
),
),
const SizedBox(
height: 15,
),
],
),
// floatingActionButton: FloatingActionButton(
// onPressed: () async {
// await model.webViewController!.reload();
// },
// backgroundColor: mainColor,
// child: const Icon(Icons.add),
// ),
),
);
},

View File

@ -1,10 +1,78 @@
import 'package:reza_admin/app/app.locator.dart';
import 'package:reza_admin/app/app.logger.dart';
import 'package:reza_admin/model/my_model.dart';
import 'package:reza_admin/model/reservasi_meja_model.dart';
import 'package:reza_admin/model/user_model.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../services/my_socket_io_client.dart';
class MejaListViewModel extends CustomBaseViewModel {
final log = getLogger('MejaListViewModel');
WebViewController? webViewController;
final socketIoClient = locator<MySocketIoClient>();
List<ReservasiMejaModel> reservasiMejaList = [];
List<UserModel?> userModelList = [];
Future<void> init() async {
globalVar.backPressed = 'exitApp';
socketIoClient.on('table_admin', (data) {
log.i('data : $data');
getData();
webViewController!.reload();
});
getData();
}
getData() async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await httpService.get('table/detail');
// log.i('response : ${response.data}');
MyModel myModel = MyModel.fromJson(response.data);
if (myModel.data != null) {
for (var item in myModel.data!) {
reservasiMejaList.add(ReservasiMejaModel.fromJson(item));
}
// log.i('reservasiMejaList : $reservasiMejaList');
}
for (var item in reservasiMejaList) {
if (item.idUser != null) {
UserModel? userModel = await getUserDetail(item.idUser!);
userModelList.add(userModel!);
} else {
userModelList.add(null);
}
}
log.i('userModelList : $userModelList');
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismiss();
}
}
Future<UserModel?> getUserDetail(int id) async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await httpService.get('table/user/$id');
log.i('response : ${response.data}');
MyModel myModel = MyModel.fromJson(response.data);
return UserModel.fromJson(myModel.data);
} catch (e) {
log.e(e);
return null;
} finally {
setBusy(false);
easyLoading.dismiss();
// return null;
}
}
}

View File

@ -131,8 +131,8 @@ class PesananListView extends StatelessWidget {
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
child: Image.asset(
'assets/nasi_goreng.jpg',
height: 100,
width: double.infinity,
fit: BoxFit.fill,

View File

@ -20,7 +20,7 @@ class LoginUserViewModel extends CustomBaseViewModel {
globalVar.backPressed = 'cantBack';
easyLoading.showLoading();
await Future.delayed(const Duration(seconds: 2));
easyLoading.dismissLoading();
easyLoading.dismiss();
setBusy(false);
globalVar.backPressed = 'backNormal';
notifyListeners();

View File

@ -1,9 +1,16 @@
import 'package:reza_admin/app/app.locator.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../services/my_socket_io_client.dart';
// import '../../../services/my_socket_io_client.dart';
class SplashScreenViewModel extends CustomBaseViewModel {
final socketIoClient = locator<MySocketIoClient>();
Future<void> init() async {
// MySocketIoClient().init();
socketIoClient.init();
await Future.delayed(const Duration(seconds: 2));
await navigationService.navigateToLoginUserView();
}

View File

@ -773,6 +773,22 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
socket_io_client:
dependency: "direct main"
description:
name: socket_io_client
sha256: ede469f3e4c55e8528b4e023bdedbc20832e8811ab9b61679d1ba3ed5f01f23b
url: "https://pub.dev"
source: hosted
version: "2.0.3+1"
socket_io_common:
dependency: transitive
description:
name: socket_io_common
sha256: "2ab92f8ff3ebbd4b353bf4a98bee45cc157e3255464b2f90f66e09c4472047eb"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
source_gen:
dependency: transitive
description:

View File

@ -55,6 +55,7 @@ dependencies:
flutter_hooks: ^0.19.0
webview_flutter: ^3.0.4
carousel_slider: ^4.2.1
socket_io_client:
dev_dependencies:
flutter_test:
@ -91,6 +92,7 @@ flutter:
- assets/reza_gazebo.jpeg
- assets/reza_meja_1.jpeg
- assets/reza_meja_2.jpeg
- assets/nasi_goreng.jpg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware