après copie des sources appli ecommerce
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
include: package:flutter_lints/flutter.yaml
|
# FBE nolint
|
||||||
|
#include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
# The lint rules applied to this project can be customized in the
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 606 B |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
|
@ -1,15 +1,53 @@
|
||||||
PODS:
|
PODS:
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- fluttertoast (0.0.2):
|
||||||
|
- Flutter
|
||||||
|
- Toast
|
||||||
|
- FMDB (2.7.5):
|
||||||
|
- FMDB/standard (= 2.7.5)
|
||||||
|
- FMDB/standard (2.7.5)
|
||||||
|
- package_info_plus (0.4.5):
|
||||||
|
- Flutter
|
||||||
|
- path_provider_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- sqflite (0.0.2):
|
||||||
|
- Flutter
|
||||||
|
- FMDB (>= 2.7.5)
|
||||||
|
- Toast (4.0.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||||
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
||||||
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- FMDB
|
||||||
|
- Toast
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
fluttertoast:
|
||||||
|
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||||
|
package_info_plus:
|
||||||
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
|
path_provider_foundation:
|
||||||
|
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
||||||
|
sqflite:
|
||||||
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
|
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||||
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
|
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||||
|
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
|
||||||
|
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||||
|
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||||
|
|
||||||
PODFILE CHECKSUM: b634fd49380cdd3837626153fb977533b1916433
|
PODFILE CHECKSUM: b634fd49380cdd3837626153fb977533b1916433
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
9A8FF4FA508F9F04A7721B23 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
|
@ -249,6 +250,23 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
|
9A8FF4FA508F9F04A7721B23 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
|
||||||
|
/*
|
||||||
|
this is constant pages
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const String APP_NAME = 'Mobile DR';
|
||||||
|
|
||||||
|
// color for apps
|
||||||
|
const Color PRIMARY_COLOR = Color(0xFF07ac12);
|
||||||
|
const Color ASSENT_COLOR = Color(0xFFe75f3f);
|
||||||
|
|
||||||
|
const Color CHARCOAL = Color(0xFF515151);
|
||||||
|
const Color BLACK_GREY = Color(0xff777777);
|
||||||
|
const Color SOFT_GREY = Color(0xFFaaaaaa);
|
||||||
|
const Color SOFT_BLUE = Color(0xff01aed6);
|
||||||
|
|
||||||
|
const String ERROR_OCCURED = 'Error occured, please try again later';
|
||||||
|
|
||||||
|
const int LIMIT_PAGE = 8;
|
||||||
|
|
||||||
|
const String GLOBAL_URL = 'https://ijtechnology.net/assets/images/api/devkit';
|
||||||
|
//const String GLOBAL_URL = 'http://192.168.0.4/devkit';
|
||||||
|
//const String GLOBAL_URL = 'http://192.168.100.9/devkit';
|
||||||
|
|
||||||
|
const String LOCAL_IMAGES_URL = 'assets/images';
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
this is global style
|
||||||
|
This file is used to styling a whole application
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class GlobalStyle {
|
||||||
|
// appBar
|
||||||
|
static const Color appBarIconThemeColor = Colors.black;
|
||||||
|
static const double appBarElevation = 0;
|
||||||
|
static const SystemUiOverlayStyle appBarSystemOverlayStyle =
|
||||||
|
SystemUiOverlayStyle.dark;
|
||||||
|
static const Color appBarBackgroundColor = Colors.white;
|
||||||
|
static const TextStyle appBarTitle =
|
||||||
|
TextStyle(fontSize: 18, color: Colors.black);
|
||||||
|
/*
|
||||||
|
this is used for height at product card using gridDelegate
|
||||||
|
if you change font size or using custom font such as google font, sometimes there is an error said
|
||||||
|
"Bottom overflowed by xx pixel" depends on the font height (Every font has a different height)
|
||||||
|
so you need to change below
|
||||||
|
*/
|
||||||
|
static const double gridDelegateRatio = 0.625; // lower is more longer
|
||||||
|
static const double gridDelegateFlashsaleRatio =
|
||||||
|
0.597; // lower is more longer
|
||||||
|
static const double horizontalProductHeightMultiplication =
|
||||||
|
1.90; // higher is more longer
|
||||||
|
|
||||||
|
// styling product card
|
||||||
|
static const TextStyle productName = TextStyle(fontSize: 12, color: CHARCOAL);
|
||||||
|
|
||||||
|
static const TextStyle productPrice =
|
||||||
|
TextStyle(fontSize: 13, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle productPriceDiscounted = TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: SOFT_GREY,
|
||||||
|
decoration: TextDecoration.lineThrough,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const TextStyle productSale =
|
||||||
|
TextStyle(fontSize: 11, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const TextStyle productLocation =
|
||||||
|
TextStyle(fontSize: 11, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const TextStyle productTotalReview =
|
||||||
|
TextStyle(fontSize: 11, color: SOFT_GREY);
|
||||||
|
|
||||||
|
// search filter
|
||||||
|
static const TextStyle filterTitle =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
// detail product
|
||||||
|
static const TextStyle detailProductPrice =
|
||||||
|
TextStyle(fontSize: 20, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle sectionTitle =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle viewAll = TextStyle(
|
||||||
|
fontSize: 13, fontWeight: FontWeight.bold, color: PRIMARY_COLOR);
|
||||||
|
|
||||||
|
static const TextStyle paymentTotalPrice =
|
||||||
|
TextStyle(fontSize: 14, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
// delivery
|
||||||
|
static const TextStyle deliveryPrice = TextStyle(fontSize: 15);
|
||||||
|
|
||||||
|
static const TextStyle deliveryTotalPrice =
|
||||||
|
TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: ASSENT_COLOR);
|
||||||
|
|
||||||
|
static const TextStyle chooseCourier =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle courierTitle =
|
||||||
|
TextStyle(fontSize: 15, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle courierType = TextStyle(fontSize: 15, color: CHARCOAL);
|
||||||
|
|
||||||
|
// shopping cart
|
||||||
|
static const TextStyle shoppingCartTotalPrice =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: ASSENT_COLOR);
|
||||||
|
|
||||||
|
static const TextStyle shoppingCartOtherProduct = TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
);
|
||||||
|
|
||||||
|
// account information
|
||||||
|
static const TextStyle accountInformationLabel =
|
||||||
|
TextStyle(fontSize: 15, color: BLACK_GREY, fontWeight: FontWeight.normal);
|
||||||
|
|
||||||
|
static const TextStyle accountInformationValue =
|
||||||
|
TextStyle(fontSize: 16, color: Colors.black);
|
||||||
|
|
||||||
|
static const TextStyle accountInformationEdit = TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: PRIMARY_COLOR,
|
||||||
|
);
|
||||||
|
|
||||||
|
// address
|
||||||
|
static const TextStyle addressTitle =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle addressContent = TextStyle(fontSize: 12);
|
||||||
|
|
||||||
|
static const TextStyle addressAction =
|
||||||
|
TextStyle(fontSize: 14, color: PRIMARY_COLOR);
|
||||||
|
|
||||||
|
// verification
|
||||||
|
static const TextStyle chooseVerificationTitle =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: CHARCOAL);
|
||||||
|
|
||||||
|
static const TextStyle chooseVerificationMessage =
|
||||||
|
TextStyle(fontSize: 13, color: BLACK_GREY);
|
||||||
|
|
||||||
|
static const TextStyle methodTitle =
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: CHARCOAL);
|
||||||
|
|
||||||
|
static const TextStyle methodMessage =
|
||||||
|
TextStyle(fontSize: 13, color: BLACK_GREY);
|
||||||
|
|
||||||
|
static const TextStyle notReceiveCode =
|
||||||
|
TextStyle(fontSize: 13, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const TextStyle resendVerification =
|
||||||
|
TextStyle(fontSize: 13, color: PRIMARY_COLOR);
|
||||||
|
|
||||||
|
static const Icon iconBack =
|
||||||
|
Icon(Icons.arrow_back, size: 16, color: PRIMARY_COLOR);
|
||||||
|
|
||||||
|
static const TextStyle back =
|
||||||
|
TextStyle(color: PRIMARY_COLOR, fontWeight: FontWeight.w700);
|
||||||
|
|
||||||
|
// authentication
|
||||||
|
static const TextStyle authTitle = TextStyle(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.bold, color: PRIMARY_COLOR);
|
||||||
|
|
||||||
|
static const TextStyle authNotes = TextStyle(fontSize: 13, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const TextStyle authSignWith =
|
||||||
|
TextStyle(fontSize: 13, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const TextStyle authBottom1 =
|
||||||
|
TextStyle(fontSize: 13, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const TextStyle authBottom2 =
|
||||||
|
TextStyle(fontSize: 13, color: PRIMARY_COLOR);
|
||||||
|
|
||||||
|
static const TextStyle resetPasswordNotes =
|
||||||
|
TextStyle(fontSize: 14, color: SOFT_GREY);
|
||||||
|
|
||||||
|
// coupon
|
||||||
|
static const TextStyle couponLimitedOffer =
|
||||||
|
TextStyle(fontSize: 11, color: Colors.white);
|
||||||
|
|
||||||
|
static const TextStyle couponName =
|
||||||
|
TextStyle(fontSize: 15, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
|
static const TextStyle couponExpired =
|
||||||
|
TextStyle(fontSize: 13, color: SOFT_GREY);
|
||||||
|
|
||||||
|
static const Icon iconTime =
|
||||||
|
Icon(Icons.access_time, size: 14, color: SOFT_GREY);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Aakash Kumar
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CircularClipper extends CustomClipper<Rect> {
|
||||||
|
final Offset? center;
|
||||||
|
final double fraction;
|
||||||
|
|
||||||
|
CircularClipper(this.fraction, [this.center]);
|
||||||
|
@override
|
||||||
|
Rect getClip(Size size) {
|
||||||
|
Rect rect = Rect.fromCircle(
|
||||||
|
center: center ?? Offset(size.width / 2, size.height / 2),
|
||||||
|
radius: size.height * this.fraction);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldReclip(CustomClipper<Rect> oldClipper) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,396 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/library/flutter_overboard/circular_clipper.dart';
|
||||||
|
import 'package:mobdr/library/flutter_overboard/overboard_animator.dart';
|
||||||
|
import 'package:mobdr/library/flutter_overboard/page_model.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum SwipeDirection { LEFT_TO_RIGHT, RIGHT_TO_LEFT, SKIP_TO_LAST }
|
||||||
|
|
||||||
|
class OverBoard extends StatefulWidget {
|
||||||
|
final List<PageModel> pages;
|
||||||
|
final Offset? center;
|
||||||
|
final bool? showBullets;
|
||||||
|
final VoidCallback finishCallback;
|
||||||
|
final VoidCallback? skipCallback;
|
||||||
|
final OverBoardAnimator? animator;
|
||||||
|
final String? skipText, nextText, finishText;
|
||||||
|
|
||||||
|
OverBoard(
|
||||||
|
{Key? key,
|
||||||
|
required this.pages,
|
||||||
|
this.center,
|
||||||
|
this.showBullets,
|
||||||
|
this.skipText,
|
||||||
|
this.nextText,
|
||||||
|
this.finishText,
|
||||||
|
required this.finishCallback,
|
||||||
|
this.animator,
|
||||||
|
this.skipCallback})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_OverBoardState createState() => _OverBoardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OverBoardState extends State<OverBoard> with TickerProviderStateMixin {
|
||||||
|
late OverBoardAnimator _animator;
|
||||||
|
|
||||||
|
ScrollController _scrollController = new ScrollController();
|
||||||
|
double _bulletPadding = 5.0, _bulletSize = 10.0, _bulletContainerWidth = 0;
|
||||||
|
|
||||||
|
int _counter = 0, _last = 0;
|
||||||
|
int _total = 0;
|
||||||
|
double initial = 0, distance = 0;
|
||||||
|
Random random = new Random();
|
||||||
|
SwipeDirection _swipeDirection = SwipeDirection.RIGHT_TO_LEFT;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_animator = new OverBoardAnimator(this);
|
||||||
|
_total = widget.pages.length;
|
||||||
|
_animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
child: _getStack(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getStack() {
|
||||||
|
return GestureDetector(
|
||||||
|
onPanStart: (DragStartDetails details) {
|
||||||
|
initial = details.globalPosition.dx;
|
||||||
|
},
|
||||||
|
onPanUpdate: (DragUpdateDetails details) {
|
||||||
|
distance = details.globalPosition.dx - initial;
|
||||||
|
},
|
||||||
|
onPanEnd: (DragEndDetails details) {
|
||||||
|
initial = 0.0;
|
||||||
|
setState(() {
|
||||||
|
_last = _counter;
|
||||||
|
});
|
||||||
|
if (distance > 1 && _counter > 0) {
|
||||||
|
setState(() {
|
||||||
|
_counter--;
|
||||||
|
_swipeDirection = SwipeDirection.LEFT_TO_RIGHT;
|
||||||
|
});
|
||||||
|
_animate();
|
||||||
|
} else if (distance < 0 && _counter < _total - 1) {
|
||||||
|
setState(() {
|
||||||
|
_counter++;
|
||||||
|
_swipeDirection = SwipeDirection.RIGHT_TO_LEFT;
|
||||||
|
});
|
||||||
|
_animate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
_getPage(_last),
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _animator.getAnimator(),
|
||||||
|
builder: (context, child) {
|
||||||
|
return ClipOval(
|
||||||
|
clipper: CircularClipper(
|
||||||
|
_animator.getAnimator().value, widget.center),
|
||||||
|
child: _getPage(_counter));
|
||||||
|
},
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: <Widget>[
|
||||||
|
Opacity(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: (widget.skipCallback != null
|
||||||
|
? widget.skipCallback
|
||||||
|
: _skip),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 18),
|
||||||
|
child: Text(widget.skipText ?? "SKIP"),
|
||||||
|
)),
|
||||||
|
opacity: (_counter < _total - 1) ? 1.0 : 0.0,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Center(child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
_bulletContainerWidth = constraints.maxWidth - 40.0;
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: ((widget.showBullets ?? true)
|
||||||
|
? SingleChildScrollView(
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
controller: _scrollController,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
for (int i = 0; i < _total; i++)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(_bulletPadding),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration:
|
||||||
|
Duration(milliseconds: 150),
|
||||||
|
height: _bulletSize,
|
||||||
|
width: (i == _counter)
|
||||||
|
? _bulletSize * 2
|
||||||
|
: _bulletSize,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: (i == _counter)
|
||||||
|
? PRIMARY_COLOR
|
||||||
|
: Colors.grey[300],
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(10))),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
(_counter < _total - 1
|
||||||
|
? TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: _next,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Text(widget.nextText ?? "NEXT"),
|
||||||
|
))
|
||||||
|
: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: widget.finishCallback,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Text(widget.finishText ?? "FINISH"),
|
||||||
|
))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPage(index) {
|
||||||
|
PageModel page = widget.pages[index];
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
color: page.color,
|
||||||
|
child: page.child != null
|
||||||
|
? Center(
|
||||||
|
child: page.doAnimateChild!
|
||||||
|
? AnimatedBoard(
|
||||||
|
animator: _animator,
|
||||||
|
child: page.child,
|
||||||
|
)
|
||||||
|
: page.child,
|
||||||
|
)
|
||||||
|
: (kIsWeb)
|
||||||
|
? SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(top: 64),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
page.doAnimateImage!
|
||||||
|
? AnimatedBoard(
|
||||||
|
animator: _animator,
|
||||||
|
child: new Padding(
|
||||||
|
padding: new EdgeInsets.only(bottom: 25.0),
|
||||||
|
child: (page.imageAssetPath != null)
|
||||||
|
? new Image.asset(page.imageAssetPath!,
|
||||||
|
width: 300.0, height: 300.0)
|
||||||
|
: buildCacheNetworkImage(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
url: page.imageFromUrl,
|
||||||
|
plColor: Colors.transparent),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: new Padding(
|
||||||
|
padding: new EdgeInsets.only(bottom: 25.0),
|
||||||
|
child: (page.imageAssetPath != null)
|
||||||
|
? new Image.asset(page.imageAssetPath!,
|
||||||
|
width: 300.0, height: 300.0)
|
||||||
|
: buildCacheNetworkImage(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
url: page.imageFromUrl,
|
||||||
|
plColor: Colors.transparent),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: new EdgeInsets.only(
|
||||||
|
top: 10.0, bottom: 30.0, left: 30.0, right: 30.0),
|
||||||
|
child: new Text(
|
||||||
|
page.title!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: new TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 24.0,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: new EdgeInsets.only(
|
||||||
|
bottom: 75.0, left: 30.0, right: 30.0),
|
||||||
|
child: new Text(
|
||||||
|
page.body!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: new TextStyle(
|
||||||
|
color: SOFT_GREY,
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
page.doAnimateImage!
|
||||||
|
? AnimatedBoard(
|
||||||
|
animator: _animator,
|
||||||
|
child: new Padding(
|
||||||
|
padding: new EdgeInsets.only(bottom: 25.0),
|
||||||
|
child: (page.imageAssetPath != null)
|
||||||
|
? new Image.asset(page.imageAssetPath!,
|
||||||
|
width: 300.0, height: 300.0)
|
||||||
|
: buildCacheNetworkImage(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
url: page.imageFromUrl,
|
||||||
|
plColor: Colors.transparent),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: new Padding(
|
||||||
|
padding: new EdgeInsets.only(bottom: 25.0),
|
||||||
|
child: (page.imageAssetPath != null)
|
||||||
|
? new Image.asset(page.imageAssetPath!,
|
||||||
|
width: 300.0, height: 300.0)
|
||||||
|
: buildCacheNetworkImage(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
url: page.imageFromUrl,
|
||||||
|
plColor: Colors.transparent),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: new EdgeInsets.only(
|
||||||
|
top: 10.0, bottom: 30.0, left: 30.0, right: 30.0),
|
||||||
|
child: new Text(
|
||||||
|
page.title!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: new TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 24.0,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: new EdgeInsets.only(
|
||||||
|
bottom: 75.0, left: 30.0, right: 30.0),
|
||||||
|
child: new Text(
|
||||||
|
page.body!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: new TextStyle(
|
||||||
|
color: SOFT_GREY,
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_next() {
|
||||||
|
setState(() {
|
||||||
|
_swipeDirection = SwipeDirection.RIGHT_TO_LEFT;
|
||||||
|
_last = _counter;
|
||||||
|
_counter++;
|
||||||
|
});
|
||||||
|
_animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
_skip() {
|
||||||
|
setState(() {
|
||||||
|
_swipeDirection = SwipeDirection.SKIP_TO_LAST;
|
||||||
|
_last = _counter;
|
||||||
|
_counter = _total - 1;
|
||||||
|
});
|
||||||
|
_animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
_animate() {
|
||||||
|
_animator.getController().forward(from: 0.0);
|
||||||
|
|
||||||
|
double _bulletDimension = (_bulletPadding * 2) + (_bulletSize);
|
||||||
|
double _scroll = _bulletDimension * _counter;
|
||||||
|
double _maxScroll = _bulletDimension * _total - 1;
|
||||||
|
if (_scroll > _bulletContainerWidth &&
|
||||||
|
_swipeDirection == SwipeDirection.RIGHT_TO_LEFT) {
|
||||||
|
double _scrollDistance =
|
||||||
|
(((_scroll - _bulletContainerWidth) ~/ _bulletDimension) + 1) *
|
||||||
|
_bulletDimension;
|
||||||
|
_scrollController.animateTo(_scrollDistance,
|
||||||
|
curve: Curves.easeIn, duration: Duration(milliseconds: 100));
|
||||||
|
} else if (_scroll < (_maxScroll - _bulletContainerWidth) &&
|
||||||
|
_swipeDirection == SwipeDirection.LEFT_TO_RIGHT) {
|
||||||
|
_scrollController.animateTo(_scroll,
|
||||||
|
curve: Curves.easeIn, duration: Duration(milliseconds: 100));
|
||||||
|
} else if (_swipeDirection == SwipeDirection.SKIP_TO_LAST) {
|
||||||
|
_scrollController.animateTo(_maxScroll,
|
||||||
|
curve: Curves.easeIn, duration: Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedBoard extends StatelessWidget {
|
||||||
|
final Widget? child;
|
||||||
|
final OverBoardAnimator? animator;
|
||||||
|
|
||||||
|
const AnimatedBoard({Key? key, this.animator, this.child}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Transform(
|
||||||
|
transform: new Matrix4.translationValues(
|
||||||
|
0.0, 50.0 * (1.0 - animator!.getAnimator().value), 0.0),
|
||||||
|
child: new Padding(
|
||||||
|
padding: new EdgeInsets.only(bottom: 25.0),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class OverBoardAnimator {
|
||||||
|
late TickerProvider _vsync;
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation _animation;
|
||||||
|
|
||||||
|
OverBoardAnimator(vsync) {
|
||||||
|
this._vsync = vsync;
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: _vsync, duration: const Duration(milliseconds: 500));
|
||||||
|
_animation = CurvedAnimation(parent: _controller, curve: Curves.easeIn);
|
||||||
|
//print('creating new animator');
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationController getController() {
|
||||||
|
return _controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation getAnimator() {
|
||||||
|
return _animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
class PageModel {
|
||||||
|
Color color;
|
||||||
|
String? imageAssetPath;
|
||||||
|
String? imageFromUrl;
|
||||||
|
String? title;
|
||||||
|
String? body;
|
||||||
|
Widget? child;
|
||||||
|
bool? doAnimateChild;
|
||||||
|
bool? doAnimateImage;
|
||||||
|
|
||||||
|
PageModel(
|
||||||
|
{required this.color,
|
||||||
|
this.imageAssetPath,
|
||||||
|
this.imageFromUrl,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
required this.doAnimateImage});
|
||||||
|
|
||||||
|
PageModel.withChild(
|
||||||
|
{required this.child,
|
||||||
|
required this.color,
|
||||||
|
required this.doAnimateChild});
|
||||||
|
}
|
||||||
133
lib/main.dart
|
|
@ -1,115 +1,48 @@
|
||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
import 'package:mobdr/ui/splash_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
// this function makes application always run in portrait mode
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
|
||||||
|
.then((_) {
|
||||||
|
runApp(MyApp());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyCustomScrollBehavior extends MaterialScrollBehavior {
|
||||||
|
// Override behavior methods and getters like dragDevices
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
// etc.
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
|
||||||
|
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Flutter Demo',
|
scrollBehavior: MyCustomScrollBehavior(),
|
||||||
|
title: APP_NAME,
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
// This is the theme of your application.
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
//
|
pageTransitionsTheme: PageTransitionsTheme(builders: {
|
||||||
// Try running your application with "flutter run". You'll see the
|
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||||
// application has a blue toolbar. Then, without quitting the app, try
|
TargetPlatform.android: ZoomPageTransitionsBuilder(),
|
||||||
// changing the primarySwatch below to Colors.green and then invoke
|
}),
|
||||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
|
||||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
|
||||||
// Notice that the counter didn't reset back to zero; the application
|
|
||||||
// is not restarted.
|
|
||||||
primarySwatch: Colors.blue,
|
|
||||||
),
|
),
|
||||||
home: const MyHomePage(title: 'Mobile DR'),
|
home: SplashScreenPage(),
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({super.key, required this.title});
|
|
||||||
|
|
||||||
// This widget is the home page of your application. It is stateful, meaning
|
|
||||||
// that it has a State object (defined below) that contains fields that affect
|
|
||||||
// how it looks.
|
|
||||||
|
|
||||||
// This class is the configuration for the state. It holds the values (in this
|
|
||||||
// case the title) provided by the parent (in this case the App widget) and
|
|
||||||
// used by the build method of the State. Fields in a Widget subclass are
|
|
||||||
// always marked "final".
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
int _counter = 0;
|
|
||||||
|
|
||||||
void _incrementCounter() {
|
|
||||||
setState(() {
|
|
||||||
// This call to setState tells the Flutter framework that something has
|
|
||||||
// changed in this State, which causes it to rerun the build method below
|
|
||||||
// so that the display can reflect the updated values. If we changed
|
|
||||||
// _counter without calling setState(), then the build method would not be
|
|
||||||
// called again, and so nothing would appear to happen.
|
|
||||||
_counter++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// This method is rerun every time setState is called, for instance as done
|
|
||||||
// by the _incrementCounter method above.
|
|
||||||
//
|
|
||||||
// The Flutter framework has been optimized to make rerunning build methods
|
|
||||||
// fast, so that you can just rebuild anything that needs updating rather
|
|
||||||
// than having to individually change instances of widgets.
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
// Here we take the value from the MyHomePage object that was created by
|
|
||||||
// the App.build method, and use it to set our appbar title.
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
// Center is a layout widget. It takes a single child and positions it
|
|
||||||
// in the middle of the parent.
|
|
||||||
child: Column(
|
|
||||||
// Column is also a layout widget. It takes a list of children and
|
|
||||||
// arranges them vertically. By default, it sizes itself to fit its
|
|
||||||
// children horizontally, and tries to be as tall as its parent.
|
|
||||||
//
|
|
||||||
// Invoke "debug painting" (press "p" in the console, choose the
|
|
||||||
// "Toggle Debug Paint" action from the Flutter Inspector in Android
|
|
||||||
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
|
|
||||||
// to see the wireframe for each widget.
|
|
||||||
//
|
|
||||||
// Column has various properties to control how it sizes itself and
|
|
||||||
// how it positions its children. Here we use mainAxisAlignment to
|
|
||||||
// center the children vertically; the main axis here is the vertical
|
|
||||||
// axis because Columns are vertical (the cross axis would be
|
|
||||||
// horizontal).
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
const Text(
|
|
||||||
'You have pushed the button this many times:',
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'$_counter',
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: _incrementCounter,
|
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
class AddressModel {
|
||||||
|
late int id;
|
||||||
|
late String title;
|
||||||
|
late String recipientName;
|
||||||
|
late String phoneNumber;
|
||||||
|
late String addressLine1;
|
||||||
|
late String addressLine2;
|
||||||
|
late String state;
|
||||||
|
late String postalCode;
|
||||||
|
late bool defaultAddress;
|
||||||
|
|
||||||
|
AddressModel({required this.id, required this.title, required this.recipientName, required this.phoneNumber, required this.addressLine1, required this.addressLine2, required this.state, required this.postalCode, required this.defaultAddress});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AddressModel> addressData = [
|
||||||
|
AddressModel(
|
||||||
|
id: 1,
|
||||||
|
title: 'Home Address',
|
||||||
|
recipientName: 'Robert Steven',
|
||||||
|
phoneNumber: '0811888999',
|
||||||
|
addressLine1: '6019 Madison St',
|
||||||
|
addressLine2: 'West New York, NJ',
|
||||||
|
state: 'USA',
|
||||||
|
postalCode: '07093',
|
||||||
|
defaultAddress: true
|
||||||
|
),
|
||||||
|
AddressModel(
|
||||||
|
id: 2,
|
||||||
|
title: 'Apartment Address',
|
||||||
|
recipientName: 'Robert Steven',
|
||||||
|
phoneNumber: '0811888999',
|
||||||
|
addressLine1: 'Meridia Park Avenue Apartments',
|
||||||
|
addressLine2: '6035 Park Ave, West New York, NJ',
|
||||||
|
state: 'USA',
|
||||||
|
postalCode: '07093',
|
||||||
|
defaultAddress: false
|
||||||
|
),
|
||||||
|
AddressModel(
|
||||||
|
id: 3,
|
||||||
|
title: 'Office Address',
|
||||||
|
recipientName: 'Robert Steven',
|
||||||
|
phoneNumber: '0811888999',
|
||||||
|
addressLine1: '6015 Van Buren Pl',
|
||||||
|
addressLine2: 'West New York, NJ',
|
||||||
|
state: 'USA',
|
||||||
|
postalCode: '07093',
|
||||||
|
defaultAddress: false
|
||||||
|
),
|
||||||
|
AddressModel(
|
||||||
|
id: 4,
|
||||||
|
title: 'Mom Address',
|
||||||
|
recipientName: 'Stephanie',
|
||||||
|
phoneNumber: '0811564855',
|
||||||
|
addressLine1: '7503 2nd Ave',
|
||||||
|
addressLine2: 'North Bergen, NJ',
|
||||||
|
state: 'USA',
|
||||||
|
postalCode: '07047',
|
||||||
|
defaultAddress: false
|
||||||
|
),
|
||||||
|
AddressModel(
|
||||||
|
id: 5,
|
||||||
|
title: 'Anthony Address',
|
||||||
|
recipientName: 'Anthony Daniel',
|
||||||
|
phoneNumber: '0811118997',
|
||||||
|
addressLine1: '223-201 62nd St',
|
||||||
|
addressLine2: 'West New York, NJ',
|
||||||
|
state: 'USA',
|
||||||
|
postalCode: '07093',
|
||||||
|
defaultAddress: false
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class CategoryAllProductModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
late int sale;
|
||||||
|
late String location;
|
||||||
|
|
||||||
|
CategoryAllProductModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review,
|
||||||
|
required this.sale,
|
||||||
|
required this.location});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CategoryAllProductModel> categoryAllProductData = [
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575',
|
||||||
|
price: 1643,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2,
|
||||||
|
sale: 3,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'iPhone SE 2020',
|
||||||
|
price: 399,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/78.png',
|
||||||
|
rating: 5,
|
||||||
|
review: 219,
|
||||||
|
sale: 23,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone',
|
||||||
|
price: 62,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 42,
|
||||||
|
sale: 69,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver',
|
||||||
|
price: 866,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 22,
|
||||||
|
sale: 468,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 5,
|
||||||
|
name:
|
||||||
|
'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone',
|
||||||
|
price: 219,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 934,
|
||||||
|
sale: 1881,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 6,
|
||||||
|
name:
|
||||||
|
'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air',
|
||||||
|
price: 22.66,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 131,
|
||||||
|
sale: 466,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'iPod Touch 2019 7th Generation - 32GB SpaceGrey MVHW2',
|
||||||
|
price: 242,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/79.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 11,
|
||||||
|
sale: 17,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray',
|
||||||
|
price: 189,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 36,
|
||||||
|
sale: 112,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 9,
|
||||||
|
name: 'NEW Original Apple TV 4K 64GB 5th Generation',
|
||||||
|
price: 261,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 98,
|
||||||
|
sale: 23,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
CategoryAllProductModel(
|
||||||
|
id: 10,
|
||||||
|
name:
|
||||||
|
'Mac Mini 3.0GHz 6-Core Processor with Turbo Boost up to 4.1GHz 512GB Storage',
|
||||||
|
price: 1099,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/77.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 78,
|
||||||
|
sale: 132,
|
||||||
|
location: 'Brooklyn')
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class CategoryBannerModel {
|
||||||
|
late int id;
|
||||||
|
late String image;
|
||||||
|
|
||||||
|
CategoryBannerModel({required this.id, required this.image});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CategoryBannerModel> categoryBannerData = [
|
||||||
|
CategoryBannerModel(
|
||||||
|
id: 1, image: GLOBAL_URL + '/apps/ecommerce/category_banner/1.jpg'),
|
||||||
|
CategoryBannerModel(
|
||||||
|
id: 2, image: GLOBAL_URL + '/apps/ecommerce/category_banner/2.jpg'),
|
||||||
|
CategoryBannerModel(
|
||||||
|
id: 3, image: GLOBAL_URL + '/apps/ecommerce/category_banner/3.jpg'),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class CategoryForYouModel {
|
||||||
|
late int id;
|
||||||
|
late String image;
|
||||||
|
|
||||||
|
CategoryForYouModel({required this.id, required this.image});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CategoryForYouModel> categoryForYouData = [
|
||||||
|
CategoryForYouModel(
|
||||||
|
id: 7,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category_for_you/1.jpg',
|
||||||
|
),
|
||||||
|
CategoryForYouModel(
|
||||||
|
id: 3, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/2.jpg'),
|
||||||
|
CategoryForYouModel(
|
||||||
|
id: 5, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/3.jpg'),
|
||||||
|
CategoryForYouModel(
|
||||||
|
id: 2, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/4.jpg'),
|
||||||
|
CategoryForYouModel(
|
||||||
|
id: 8, image: GLOBAL_URL + '/apps/ecommerce/category_for_you/5.jpg'),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class CategoryModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late String image;
|
||||||
|
|
||||||
|
CategoryModel({required this.id, required this.name, required this.image});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Category Data Information
|
||||||
|
width = 110px
|
||||||
|
height = 110px
|
||||||
|
*/
|
||||||
|
List<CategoryModel> categoryData = [
|
||||||
|
CategoryModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'Fashion',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/fashion.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'Smartphone & Tablets',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/smartphone.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'Electronic',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/electronic.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'Otomotif',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/otomotif.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'Sport',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/sport.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'Food',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/food.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'Voucher\nGame',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/voucher_game.png',
|
||||||
|
),
|
||||||
|
CategoryModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'Health & Care',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/category/health.png',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class CategoryNewProductModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
|
||||||
|
CategoryNewProductModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CategoryNewProductModel> categoryNewProductData = [
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 1,
|
||||||
|
name:
|
||||||
|
'Converter APPLE USB-C To 3.5 mm Headphone Jack Adapter New Ipad Pro',
|
||||||
|
price: 10,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/71.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 54),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 2,
|
||||||
|
name:
|
||||||
|
'Charger adapter New Macbook Pro Retina 13 inch Apple 2017 2018 61w ori',
|
||||||
|
price: 46,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/72.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 4),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray',
|
||||||
|
price: 189,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 24),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'Apple iMac 2020 4K 21.5" inch i3 3.6GHz /8GB/256GB MHK23',
|
||||||
|
price: 1369,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/74.jpg',
|
||||||
|
rating: 0,
|
||||||
|
review: 0),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'Leather Sleeve for 13-inch MacBook Air and MacBook Pro - Black',
|
||||||
|
price: 179,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/75.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 120),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'Magic Mouse 2 - Space Gray',
|
||||||
|
price: 99,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/76.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 174),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 7,
|
||||||
|
name:
|
||||||
|
'Mac Mini 3.0GHz 6-Core Processor with Turbo Boost up to 4.1GHz 512GB Storage',
|
||||||
|
price: 1099,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/77.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 78),
|
||||||
|
CategoryNewProductModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'iPhone SE 2020',
|
||||||
|
price: 399,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/78.png',
|
||||||
|
rating: 5,
|
||||||
|
review: 219),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class CategoryTrendingProductModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CategoryTrendingProductModel> categoryTrendingProductData = [
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone',
|
||||||
|
price: 62,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 42,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver',
|
||||||
|
price: 866,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 22,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'iphone 7 Plus / 7+ 128GB',
|
||||||
|
price: 433,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 129,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575',
|
||||||
|
price: 1643,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 5,
|
||||||
|
name:
|
||||||
|
'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone',
|
||||||
|
price: 219,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 934,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air',
|
||||||
|
price: 22.66,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 131,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'NEW Original Apple TV 4K 64GB 5th Generation',
|
||||||
|
price: 261,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 98,
|
||||||
|
),
|
||||||
|
CategoryTrendingProductModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'Apple Pencil 1 - Original Apple Garansi Resmi Inter',
|
||||||
|
price: 99,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/70.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 8)
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
In this model, we give example about using private variable
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ChatModel {
|
||||||
|
int _id;
|
||||||
|
String? _type;
|
||||||
|
String _isTextImageDate;
|
||||||
|
String _message;
|
||||||
|
String? _date;
|
||||||
|
bool? _read;
|
||||||
|
|
||||||
|
ChatModel(this._id, this._type, this._isTextImageDate, this._message, this._date, this._read);
|
||||||
|
|
||||||
|
int get getId{
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get getType{
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get getTextImageDate{
|
||||||
|
return _isTextImageDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get getMessage{
|
||||||
|
return _message;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get getDate{
|
||||||
|
return _date;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool? get getRead{
|
||||||
|
return _read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
class CouponModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late String day;
|
||||||
|
late String term;
|
||||||
|
|
||||||
|
CouponModel({required this.id, required this.name, required this.day, required this.term});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CouponModel> couponData =[
|
||||||
|
CouponModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'FASHION50',
|
||||||
|
day: '2',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'EVERY20',
|
||||||
|
day: '6',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'COC40',
|
||||||
|
day: '3',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'PUBG70',
|
||||||
|
day: '1',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'HEADSET10',
|
||||||
|
day: '1',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'ENDOFMONTH',
|
||||||
|
day: '5',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'LUCKYPRIZED',
|
||||||
|
day: '7',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'RANDOMSALE',
|
||||||
|
day: '14',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 9,
|
||||||
|
name: 'HEALTH15',
|
||||||
|
day: '3',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
CouponModel(
|
||||||
|
id: 10,
|
||||||
|
name: 'RIDER35',
|
||||||
|
day: '4',
|
||||||
|
term: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n'+
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n'+
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n'+
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class FlashsaleModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late int discount;
|
||||||
|
late double countItem;
|
||||||
|
late int sale;
|
||||||
|
|
||||||
|
FlashsaleModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.discount,
|
||||||
|
required this.countItem,
|
||||||
|
required this.sale});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Flashsale Data Information
|
||||||
|
width = 700px
|
||||||
|
height = 700px
|
||||||
|
*/
|
||||||
|
List<FlashsaleModel> flashsaleData = [
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 1,
|
||||||
|
name:
|
||||||
|
'BARDI Smart IP Camera CCTV Wifi IoT HomeAutomation Support iOS Android',
|
||||||
|
price: 32.3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/1.jpg',
|
||||||
|
discount: 35,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 17),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'TEROPONG MINI 30 X 60 BINOCULARS HD NIGHT VERSION 30 X 60',
|
||||||
|
price: 9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/2.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 19),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 3,
|
||||||
|
name:
|
||||||
|
'CAFELE Premium Light Glass Case - iPhone 11 Pro iPhone 11 Pro Max - iP 11 Pro Max',
|
||||||
|
price: 12.85,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/3.jpg',
|
||||||
|
discount: 30,
|
||||||
|
countItem: 60,
|
||||||
|
sale: 13),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 4,
|
||||||
|
name:
|
||||||
|
'Logitech G502 Hero / Mouse Logitech G 502 Hero Original Garansi Resmi',
|
||||||
|
price: 68.25,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/4.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 130,
|
||||||
|
sale: 70),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'Pioneer SE-C5TW TWS Bluetooth Truly Wireless Earphones - Hitam',
|
||||||
|
price: 56.85,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/5.jpg',
|
||||||
|
discount: 15,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 10),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 6,
|
||||||
|
name:
|
||||||
|
'Anker SoundCore Life Note Wireless Earbuds Bluetooth Earphones A3908 - Hitam',
|
||||||
|
price: 76.6,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/6.jpg',
|
||||||
|
discount: 40,
|
||||||
|
countItem: 130,
|
||||||
|
sale: 99),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 7,
|
||||||
|
name:
|
||||||
|
'Audio Technica AT-LP60X Fully Automatic Belt-Drive Stereo Turntable',
|
||||||
|
price: 215.58,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/7.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 40,
|
||||||
|
sale: 30),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'Xiaomi Deerma CM800 UV Anti Mite Vacuum Cleaner Dust Bed Sofa',
|
||||||
|
price: 44.28,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/8.jpg',
|
||||||
|
discount: 30,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 15),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 9,
|
||||||
|
name:
|
||||||
|
'Sony SRS- XB12 / XB 12 Extra Bass Portable Bluetooth Speaker - Black',
|
||||||
|
price: 53.33,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/9.jpg',
|
||||||
|
discount: 10,
|
||||||
|
countItem: 41,
|
||||||
|
sale: 2),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 10,
|
||||||
|
name:
|
||||||
|
'Changhong Google certified Android Smart TV 32 inch 32H4 LED TV-L32H4',
|
||||||
|
price: 240.61,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/10.jpg',
|
||||||
|
discount: 46,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 18),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 11,
|
||||||
|
name: 'READY DJI Mavic Air Fly More Combo',
|
||||||
|
price: 776.67,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/11.jpg',
|
||||||
|
discount: 10,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 20),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 12,
|
||||||
|
name:
|
||||||
|
'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black',
|
||||||
|
price: 391.76,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg',
|
||||||
|
discount: 15,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 19),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 13,
|
||||||
|
name:
|
||||||
|
'QZSD Q-202F - Flat Lay Tripod - Transverse Center Column FREE Holder U',
|
||||||
|
price: 40.74,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/13.jpg',
|
||||||
|
discount: 10,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 7),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 14,
|
||||||
|
name:
|
||||||
|
'WD My Passport 2TB Hardisk Eksternal 2.5" USB 3.0 HDD External Baru - 1TB, Merah',
|
||||||
|
price: 63.3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/14.jpg',
|
||||||
|
discount: 40,
|
||||||
|
countItem: 14,
|
||||||
|
sale: 10),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 15,
|
||||||
|
name:
|
||||||
|
'SanDisk 128GB Kartu Memori 100MB/S Ultra Microsd SD With Adapter Card',
|
||||||
|
price: 12,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/15.jpg',
|
||||||
|
discount: 40,
|
||||||
|
countItem: 140,
|
||||||
|
sale: 96),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 16,
|
||||||
|
name: 'SanDisk Extreme Pro USB 3.1 Solid State Flash Drive 256GB',
|
||||||
|
price: 120,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/16.png',
|
||||||
|
discount: 40,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 4),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 17,
|
||||||
|
name: 'Harley Davidson Skull & Flames Nylon Bomber Jacket',
|
||||||
|
price: 104.16,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/17.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 60,
|
||||||
|
sale: 50),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 18,
|
||||||
|
name:
|
||||||
|
'video intercom doorbell Wireless wifi HD Res - Cloud Storage ESCAM V6',
|
||||||
|
price: 55.825,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/18.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 17),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 19,
|
||||||
|
name:
|
||||||
|
'DEADBOLT DOOR LOCK SMART DOOR LOCK / SMART LOCK DOOR MEREK SEYVEN - EZ-TTLOCK',
|
||||||
|
price: 150,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/19.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 6),
|
||||||
|
FlashsaleModel(
|
||||||
|
id: 20,
|
||||||
|
name: 'BT-RIDER" GPS NAVIGATION ANDROID 6.0, 5 in WATERPROOF',
|
||||||
|
price: 191.66,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/20.jpg',
|
||||||
|
discount: 20,
|
||||||
|
countItem: 20,
|
||||||
|
sale: 15),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class HomeBannerModel {
|
||||||
|
late int id;
|
||||||
|
late String image;
|
||||||
|
|
||||||
|
HomeBannerModel({required this.id, required this.image});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Banner Data Information
|
||||||
|
width = 800px
|
||||||
|
height = 600px
|
||||||
|
*/
|
||||||
|
List<HomeBannerModel> homeBannerData = [
|
||||||
|
HomeBannerModel(
|
||||||
|
id: 1, image: GLOBAL_URL + '/apps/ecommerce/home_banner/1.jpg'),
|
||||||
|
HomeBannerModel(
|
||||||
|
id: 2, image: GLOBAL_URL + '/apps/ecommerce/home_banner/2.jpg'),
|
||||||
|
HomeBannerModel(
|
||||||
|
id: 3, image: GLOBAL_URL + '/apps/ecommerce/home_banner/3.jpg'),
|
||||||
|
HomeBannerModel(
|
||||||
|
id: 4, image: GLOBAL_URL + '/apps/ecommerce/home_banner/4.jpg'),
|
||||||
|
HomeBannerModel(
|
||||||
|
id: 5, image: GLOBAL_URL + '/apps/ecommerce/home_banner/5.jpg'),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class HomeTrendingModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late String image;
|
||||||
|
late String sale;
|
||||||
|
|
||||||
|
HomeTrendingModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.image,
|
||||||
|
required this.sale});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Home Trending Data Information
|
||||||
|
width = 700px
|
||||||
|
height = 700px
|
||||||
|
*/
|
||||||
|
List<HomeTrendingModel> homeTrendingData = [
|
||||||
|
HomeTrendingModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'Adidas Shirt',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/21.jpg',
|
||||||
|
sale: '12.7k'),
|
||||||
|
HomeTrendingModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'iPhone SE 2020',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/22.jpg',
|
||||||
|
sale: '8k'),
|
||||||
|
HomeTrendingModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'Macbook Air 2020',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/23.jpg',
|
||||||
|
sale: '31.4k'),
|
||||||
|
HomeTrendingModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'Gaming Chair',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/24.jpg',
|
||||||
|
sale: '11.9k'),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,219 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class LastSearchModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
late int sale;
|
||||||
|
late String location;
|
||||||
|
|
||||||
|
LastSearchModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review,
|
||||||
|
required this.sale,
|
||||||
|
required this.location});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Last Search Data Information
|
||||||
|
width = 700px
|
||||||
|
height = 700px
|
||||||
|
*/
|
||||||
|
List<LastSearchModel> lastSearchData = [
|
||||||
|
LastSearchModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'Delta Boots Import 8 Inch',
|
||||||
|
price: 18.3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/25.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 212,
|
||||||
|
sale: 735,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'Fimi X8 SE Black',
|
||||||
|
price: 567,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/26.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 63,
|
||||||
|
sale: 115,
|
||||||
|
location: 'Brooklyn',
|
||||||
|
),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'Guess Collection Watch Ceramic Type GC 6004 ',
|
||||||
|
price: 52,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/27.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 7,
|
||||||
|
sale: 7,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'Adidas Football Predator 19.3 FG F35594 Original',
|
||||||
|
price: 9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/45.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 30,
|
||||||
|
sale: 70,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'NEW Original Apple TV 4K 64GB 5th Generation',
|
||||||
|
price: 261,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 98,
|
||||||
|
sale: 263,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'SAMSUNG GALAXY S20 PLUS RAM 8/128GB',
|
||||||
|
price: 751,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/30.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 14,
|
||||||
|
sale: 17,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 7,
|
||||||
|
name:
|
||||||
|
'Changhong Google certified Android Smart TV 32 inch 32H4 LED TV-L32H4',
|
||||||
|
price: 129.9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/31.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 701,
|
||||||
|
sale: 1558,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'Adidas EQT Adv Premium Original',
|
||||||
|
price: 28.67,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/32.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 146,
|
||||||
|
sale: 398,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 9,
|
||||||
|
name:
|
||||||
|
'Xiaomi Air Purifier 3 Mijia OLED Touch Sterilization Air Ionizer - 3',
|
||||||
|
price: 139,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/33.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 275,
|
||||||
|
sale: 1055,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 10,
|
||||||
|
name: 'Spatula Set Stainless Kitchen Tools',
|
||||||
|
price: 2.5,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/34.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 302,
|
||||||
|
sale: 752,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 11,
|
||||||
|
name:
|
||||||
|
'DATA CABLE TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M',
|
||||||
|
price: 3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/35.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 636,
|
||||||
|
sale: 2087,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 12,
|
||||||
|
name: 'BASEUS QUICK CHARGER HEAD QC3.0/4.0 TYPE-C+USB 30W PD 5A - USB TC',
|
||||||
|
price: 10.6,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/36.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2802,
|
||||||
|
sale: 7052,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 13,
|
||||||
|
name: 'Xiaomi Powerbank MI2C 20000mAh Mi Power Bank 20000 mAh PLM06ZM',
|
||||||
|
price: 19.9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/37.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 105,
|
||||||
|
sale: 227,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 14,
|
||||||
|
name:
|
||||||
|
'3D FASHION MASK WITH BREATHING VALVE / MASKER PM 2.5 KARBON / WASHABLE - BLACK NEW MODEL',
|
||||||
|
price: 2.33,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/38.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 503,
|
||||||
|
sale: 3645,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 15,
|
||||||
|
name:
|
||||||
|
'Robot RT-US04 Table Phone Holder Stand Aluminium Alloy Universal - Pink',
|
||||||
|
price: 5.3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/39.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1095,
|
||||||
|
sale: 3400,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 16,
|
||||||
|
name: 'Tactical Pants Blackhawk Helikon ',
|
||||||
|
price: 10,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/40.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 63,
|
||||||
|
sale: 131,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 17,
|
||||||
|
name:
|
||||||
|
'Sony SRS- XB12 / XB 12 Extra Bass Portable Bluetooth Speaker - Black',
|
||||||
|
price: 48,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/41.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 182,
|
||||||
|
sale: 427,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 18,
|
||||||
|
name:
|
||||||
|
'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air',
|
||||||
|
price: 22.66,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 131,
|
||||||
|
sale: 466,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 19,
|
||||||
|
name:
|
||||||
|
'Macbook Pro 2019 TouchBar MV912 15" 16GB 512GB 2.3GHz 8-core i9 Gray',
|
||||||
|
price: 2212,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/43.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 16,
|
||||||
|
sale: 37,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSearchModel(
|
||||||
|
id: 20,
|
||||||
|
name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575',
|
||||||
|
price: 1643,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2,
|
||||||
|
sale: 3,
|
||||||
|
location: 'Brooklyn')
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class LastSeenModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
late int sale;
|
||||||
|
late String location;
|
||||||
|
|
||||||
|
LastSeenModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review,
|
||||||
|
required this.sale,
|
||||||
|
required this.location});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LastSeenModel> lastSeenData = [
|
||||||
|
LastSeenModel(
|
||||||
|
id: 1,
|
||||||
|
name:
|
||||||
|
'BARDI Smart IP Camera CCTV Wifi IoT HomeAutomation Support iOS Android',
|
||||||
|
price: 21,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/1.jpg',
|
||||||
|
rating: 4,
|
||||||
|
review: 462,
|
||||||
|
sale: 1543,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'iphone 7 Plus / 7+ 128GB',
|
||||||
|
price: 433,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 129,
|
||||||
|
sale: 310,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 3,
|
||||||
|
name:
|
||||||
|
'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black',
|
||||||
|
price: 333,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 13,
|
||||||
|
sale: 33,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'TEROPONG MINI 30 X 60 BINOCULARS HD NIGHT VERSION 30 X 60',
|
||||||
|
price: 7.2,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/2.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1,
|
||||||
|
sale: 2,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 5,
|
||||||
|
name:
|
||||||
|
'WD My Passport 2TB Hardisk Eksternal 2.5" USB 3.0 HDD External Baru - 1TB, Merah',
|
||||||
|
price: 9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/14.jpg',
|
||||||
|
rating: 4,
|
||||||
|
review: 71,
|
||||||
|
sale: 152,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 6,
|
||||||
|
name:
|
||||||
|
'SanDisk 128GB Kartu Memori 100MB/S Ultra Microsd SD With Adapter Card',
|
||||||
|
price: 7.2,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/15.jpg',
|
||||||
|
rating: 4,
|
||||||
|
review: 656,
|
||||||
|
sale: 1760,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 7,
|
||||||
|
name:
|
||||||
|
'BARDI Smart UNIVERSAL IR REMOTE Wifi Wireless IoT For Home Automation',
|
||||||
|
price: 11.5,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/60.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1438,
|
||||||
|
sale: 4956,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 8,
|
||||||
|
name:
|
||||||
|
'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone',
|
||||||
|
price: 219,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 934,
|
||||||
|
sale: 1881,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 9,
|
||||||
|
name:
|
||||||
|
'DATA CABLE TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M',
|
||||||
|
price: 3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/35.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 636,
|
||||||
|
sale: 2087,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 10,
|
||||||
|
name:
|
||||||
|
'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air',
|
||||||
|
price: 22.66,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 131,
|
||||||
|
sale: 466,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 11,
|
||||||
|
name:
|
||||||
|
'iPhone XS Max / XS / X / XR Case Spigen Clear Anti Shock Ultra Hybrid - Matte Black, XS Max',
|
||||||
|
price: 16.6,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/62.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2201,
|
||||||
|
sale: 5154,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 12,
|
||||||
|
name: 'TORCH WAIST BAG VALLEJO TOSCA',
|
||||||
|
price: 4.1,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/63.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 130,
|
||||||
|
sale: 302,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 13,
|
||||||
|
name: 'Bushnell Speed Gun Velocity 101911',
|
||||||
|
price: 180,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/64.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 4,
|
||||||
|
sale: 14,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 14,
|
||||||
|
name: 'Iphone XR - COPPER Tempered Glass Full Glue PREMIUM Glossy',
|
||||||
|
price: 3.1,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/65.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 130,
|
||||||
|
sale: 460,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 15,
|
||||||
|
name: 'Digital Thermometer infrared Termometer Gun',
|
||||||
|
price: 9.9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/66.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 270,
|
||||||
|
sale: 1287,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 16,
|
||||||
|
name: "L'Oreal Paris Fall Resist Hair Mask",
|
||||||
|
price: 9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/67.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 947,
|
||||||
|
sale: 3750,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 17,
|
||||||
|
name:
|
||||||
|
'Samyang Noodle Wholesaler 1 Box (40 Pcs) Hot Spicy Chicken - Original Jan 21',
|
||||||
|
price: 26,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/68.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 4,
|
||||||
|
sale: 22,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 18,
|
||||||
|
name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone',
|
||||||
|
price: 62,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 42,
|
||||||
|
sale: 69,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 19,
|
||||||
|
name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus',
|
||||||
|
price: 1152,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1,
|
||||||
|
sale: 2,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
LastSeenModel(
|
||||||
|
id: 20,
|
||||||
|
name: 'Nitendo Switch Console New HAC-001(-01) Neon Blue - Neon Red',
|
||||||
|
price: 349,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/52.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 30,
|
||||||
|
sale: 86,
|
||||||
|
location: 'Brooklyn')
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class OrderListModel {
|
||||||
|
late int id;
|
||||||
|
late String invoice;
|
||||||
|
late String date;
|
||||||
|
late String status;
|
||||||
|
late String name;
|
||||||
|
late String image;
|
||||||
|
late double payment;
|
||||||
|
|
||||||
|
OrderListModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.invoice,
|
||||||
|
required this.date,
|
||||||
|
required this.status,
|
||||||
|
required this.name,
|
||||||
|
required this.image,
|
||||||
|
required this.payment});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OrderListModel> orderListData = [
|
||||||
|
OrderListModel(
|
||||||
|
id: 1,
|
||||||
|
invoice: 'INV385739475',
|
||||||
|
date: '12 August 2020',
|
||||||
|
status: 'On Process',
|
||||||
|
name: 'Logitech G502 Hero / Mouse Logitech G 502 Hero Original',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/4.jpg',
|
||||||
|
payment: 80,
|
||||||
|
),
|
||||||
|
OrderListModel(
|
||||||
|
id: 2,
|
||||||
|
invoice: 'INV385714262',
|
||||||
|
date: '3 September 2020',
|
||||||
|
status: 'Order Completed',
|
||||||
|
name: 'Delta Boots Import 8 Inch',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/25.jpg',
|
||||||
|
payment: 36,
|
||||||
|
),
|
||||||
|
OrderListModel(
|
||||||
|
id: 3,
|
||||||
|
invoice: 'INV385776588',
|
||||||
|
date: '9 September 2020',
|
||||||
|
status: 'On Delivery',
|
||||||
|
name: 'Spatula Set Stainless Kitchen Tools',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/34.jpg',
|
||||||
|
payment: 9.6,
|
||||||
|
),
|
||||||
|
OrderListModel(
|
||||||
|
id: 4,
|
||||||
|
invoice: 'INV385798021',
|
||||||
|
date: '13 September 2020',
|
||||||
|
status: 'Waiting for payment',
|
||||||
|
name: 'Original 100% 60W Magsafe 1 Power Adapter Charger Macbook Pro - Air',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/42.jpg',
|
||||||
|
payment: 38,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class RecomendedProductModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
late int sale;
|
||||||
|
late String location;
|
||||||
|
|
||||||
|
RecomendedProductModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review,
|
||||||
|
required this.sale,
|
||||||
|
required this.location});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Recomended Product Data Information
|
||||||
|
width = 700px
|
||||||
|
height = 700px
|
||||||
|
*/
|
||||||
|
List<RecomendedProductModel> recomendedProductData = [
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'iphone 7 Plus / 7+ 128GB',
|
||||||
|
price: 433,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 129,
|
||||||
|
sale: 310,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 2,
|
||||||
|
name:
|
||||||
|
'Macbook Pro 2019 TouchBar MV912 15" 16GB 512GB 2.3GHz 8-core i9 Gray',
|
||||||
|
price: 2212,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/43.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 16,
|
||||||
|
sale: 37,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575',
|
||||||
|
price: 1643,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2,
|
||||||
|
sale: 3,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 4,
|
||||||
|
name:
|
||||||
|
'DEADBOLT DOOR LOCK SMART DOOR LOCK / SMART LOCK DOOR MEREK SEYVEN - EZ-TTLOCK',
|
||||||
|
price: 120,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/19.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 4,
|
||||||
|
sale: 6,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone',
|
||||||
|
price: 62,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 42,
|
||||||
|
sale: 69,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'SAMSUNG GALAXY S20 PLUS RAM 8/128GB',
|
||||||
|
price: 751,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/30.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 14,
|
||||||
|
sale: 17,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'Garmin Instinct Tactical - Black',
|
||||||
|
price: 290,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/47.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 13,
|
||||||
|
sale: 23,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus',
|
||||||
|
price: 1152,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1,
|
||||||
|
sale: 2,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 9,
|
||||||
|
name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver',
|
||||||
|
price: 866,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 22,
|
||||||
|
sale: 468,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 10,
|
||||||
|
name: 'Folding Bike 20 GENIO BY United Bike',
|
||||||
|
price: 173,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/50.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 9,
|
||||||
|
sale: 23,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 11,
|
||||||
|
name: 'XBOX 360 SLIM 500 GB RGH FULL GAME & KINECT',
|
||||||
|
price: 183,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/51.jpg',
|
||||||
|
rating: 4,
|
||||||
|
review: 3,
|
||||||
|
sale: 9,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 12,
|
||||||
|
name: 'Nitendo Switch Console New HAC-001(-01) Neon Blue - Neon Red',
|
||||||
|
price: 349,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/52.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 30,
|
||||||
|
sale: 86,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 13,
|
||||||
|
name: 'TP-Link TL-WR840N (V4.0) : 300Mbps TPLink WiFi Wireless N Router',
|
||||||
|
price: 11,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/53.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1075,
|
||||||
|
sale: 3247,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 14,
|
||||||
|
name: 'Google Chromecast 3 Chrome Cast 3rd HDMI Streaming',
|
||||||
|
price: 38,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/54.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 160,
|
||||||
|
sale: 574,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 15,
|
||||||
|
name: 'PRINTER CANON PIXMA MG2570S / PRINTER ALL IN ONE MG 2570 S',
|
||||||
|
price: 35,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/55.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 126,
|
||||||
|
sale: 285,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 16,
|
||||||
|
name: 'Air Jordan 1 Mid Chicago Black Toe 554724-069 100% Authentic - 43',
|
||||||
|
price: 220,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/56.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 15,
|
||||||
|
sale: 24,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 17,
|
||||||
|
name: 'ADIDAS ICE DIVE PARFUM ORIGINAL 100ML',
|
||||||
|
price: 4.9,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/57.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 251,
|
||||||
|
sale: 751,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 18,
|
||||||
|
name:
|
||||||
|
'BARDI Smart Light Bulb Lamp Bohlam LED WIFI RGBWW 12W 12 watt Home IoT',
|
||||||
|
price: 8.6,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/58.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 354,
|
||||||
|
sale: 540,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 19,
|
||||||
|
name:
|
||||||
|
'Xiaomi Yi Dash Camera Nightscape 1080P 60FPS ADAS Night Vision Dashcam',
|
||||||
|
price: 46.6,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/59.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 47,
|
||||||
|
sale: 146,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
RecomendedProductModel(
|
||||||
|
id: 20,
|
||||||
|
name:
|
||||||
|
'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black',
|
||||||
|
price: 333,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 13,
|
||||||
|
sale: 33,
|
||||||
|
location: 'Brooklyn')
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class RelatedProductModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
|
||||||
|
RelatedProductModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RelatedProductModel> relatedProductData = [
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 1,
|
||||||
|
name:
|
||||||
|
'Converter APPLE USB-C To 3.5 mm Headphone Jack Adapter New Ipad Pro',
|
||||||
|
price: 10,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/71.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 54),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 2,
|
||||||
|
name:
|
||||||
|
'Charger adapter New Macbook Pro Retina 13 inch Apple 2017 2018 61w ori',
|
||||||
|
price: 46,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/72.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 4),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray',
|
||||||
|
price: 189,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 24),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'Apple iMac 2020 4K 21.5" inch i3 3.6GHz /8GB/256GB MHK23',
|
||||||
|
price: 1369,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/74.jpg',
|
||||||
|
rating: 0,
|
||||||
|
review: 0),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'Leather Sleeve for 13-inch MacBook Air and MacBook Pro - Black',
|
||||||
|
price: 179,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/75.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 120),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'Magic Mouse 2 - Space Gray',
|
||||||
|
price: 99,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/76.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 174),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 7,
|
||||||
|
name:
|
||||||
|
'Mac Mini 3.0GHz 6-Core Processor with Turbo Boost up to 4.1GHz 512GB Storage',
|
||||||
|
price: 1099,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/77.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 78),
|
||||||
|
RelatedProductModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'iPhone SE 2020',
|
||||||
|
price: 399,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/78.png',
|
||||||
|
rating: 5,
|
||||||
|
review: 219),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
class ReviewModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late String date;
|
||||||
|
late double rating;
|
||||||
|
late String review;
|
||||||
|
|
||||||
|
ReviewModel({required this.id, required this.name, required this.date, required this.rating, required this.review});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ReviewModel> reviewData =[
|
||||||
|
ReviewModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'A*******i',
|
||||||
|
date: '11 September 2020',
|
||||||
|
rating: 5,
|
||||||
|
review: 'Everything came in time. Very well packed. Quality is excellent. Thank you!'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'L***a',
|
||||||
|
date: '10 September 2020',
|
||||||
|
rating: 5,
|
||||||
|
review: 'The goods came very quickly, in perfect condition, everything is packed, nothing is damaged! Excellent, visibility super, I recommend the seller, the store, the goods!!!'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'R******i',
|
||||||
|
date: '8 September 2020',
|
||||||
|
rating: 5,
|
||||||
|
review: 'Before Moscow 3 weeks, asked the seller that there would be no pictogram, made without them. The packaging is definitely good'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'D***s',
|
||||||
|
date: '7 September 2020',
|
||||||
|
rating: 4,
|
||||||
|
review: 'The goods arrived 21 days before perm. Packing on the photo. the box inside is crumpled. Thank you seller'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'S*******a',
|
||||||
|
date: '7 September 2020',
|
||||||
|
rating: 5,
|
||||||
|
review: 'An excellent device, its money costs. Packed perfectly, no damage. To the seller 5 +++.'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'B*******g',
|
||||||
|
date: '6 September 2020',
|
||||||
|
rating: 5,
|
||||||
|
review: 'The goods are satisfied'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'H*********a',
|
||||||
|
date: '4 September 2020',
|
||||||
|
rating: 3,
|
||||||
|
review: 'Good product, seller recommend'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'P***u',
|
||||||
|
date: '4 September 2020',
|
||||||
|
rating: 3,
|
||||||
|
review: 'Not as good as they write but still worth'
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 9,
|
||||||
|
name: 'O**********a',
|
||||||
|
date: '3 September 2020',
|
||||||
|
rating: 4,
|
||||||
|
review: "Perfect. Immediately turned up in flight. I can't give you 5 stars for the parcel. And arrived very deteriorated, but fortunately the drone works"
|
||||||
|
),
|
||||||
|
ReviewModel(
|
||||||
|
id: 10,
|
||||||
|
name: 'D********l',
|
||||||
|
date: '1 September 2020',
|
||||||
|
rating: 5,
|
||||||
|
review: 'All OK. Although well packed, the box crushed'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
class SearchModel {
|
||||||
|
late int id;
|
||||||
|
late String words;
|
||||||
|
|
||||||
|
SearchModel({required this.id, required this.words});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SearchModel> searchData = [
|
||||||
|
SearchModel(
|
||||||
|
id: 1,
|
||||||
|
words: 'adidas shirt'
|
||||||
|
),
|
||||||
|
SearchModel(
|
||||||
|
id: 2,
|
||||||
|
words: 'led tv'
|
||||||
|
),
|
||||||
|
SearchModel(
|
||||||
|
id: 3,
|
||||||
|
words: 'apple mac'
|
||||||
|
),
|
||||||
|
SearchModel(
|
||||||
|
id: 4,
|
||||||
|
words: 'iphone'
|
||||||
|
),
|
||||||
|
SearchModel(
|
||||||
|
id: 5,
|
||||||
|
words: 'asus'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class SearchProductModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
late int sale;
|
||||||
|
late String location;
|
||||||
|
|
||||||
|
SearchProductModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review,
|
||||||
|
required this.sale,
|
||||||
|
required this.location});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SearchProductModel> searchProductData = [
|
||||||
|
SearchProductModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'iphone 7 Plus / 7+ 128GB',
|
||||||
|
price: 433,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/28.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 129,
|
||||||
|
sale: 310,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone',
|
||||||
|
price: 62,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 42,
|
||||||
|
sale: 69,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'New imac 2017 MNEA2 5K retina /3,5GHZ/i5/8GB/1TB/RP575',
|
||||||
|
price: 1643,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/44.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 2,
|
||||||
|
sale: 3,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 4,
|
||||||
|
name:
|
||||||
|
'APPLE AIRPODS PRO WITH WIRELESS CHARGING ORIGINAL - AIRPOD - Free Silicone',
|
||||||
|
price: 219,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/61.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 934,
|
||||||
|
sale: 1881,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'NEW Apple Magic Trackpad 2 Space Grey MRMF2 Gray',
|
||||||
|
price: 189,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/73.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 36,
|
||||||
|
sale: 112,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 6,
|
||||||
|
name: 'NEW Original Apple TV 4K 64GB 5th Generation',
|
||||||
|
price: 261,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/29.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 98,
|
||||||
|
sale: 23,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus',
|
||||||
|
price: 1152,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1,
|
||||||
|
sale: 2,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 8,
|
||||||
|
name: 'Garmin Instinct Tactical - Black',
|
||||||
|
price: 290,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/47.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 13,
|
||||||
|
sale: 23,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 9,
|
||||||
|
name: 'XBOX 360 SLIM 500 GB RGH FULL GAME & KINECT',
|
||||||
|
price: 183,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/51.jpg',
|
||||||
|
rating: 4,
|
||||||
|
review: 3,
|
||||||
|
sale: 9,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 10,
|
||||||
|
name: 'TP-Link TL-WR840N (V4.0) : 300Mbps TPLink WiFi Wireless N Router',
|
||||||
|
price: 11,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/53.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1075,
|
||||||
|
sale: 3247,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 11,
|
||||||
|
name:
|
||||||
|
'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black',
|
||||||
|
price: 333,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 13,
|
||||||
|
sale: 33,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 12,
|
||||||
|
name: 'Leather Sleeve for 13-inch MacBook Air and MacBook Pro - Black',
|
||||||
|
price: 179,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/75.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 120,
|
||||||
|
sale: 131,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 13,
|
||||||
|
name: 'Apple iMac 2020 4K 21.5" inch i3 3.6GHz /8GB/256GB MHK23',
|
||||||
|
price: 1369,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/74.jpg',
|
||||||
|
rating: 0,
|
||||||
|
review: 0,
|
||||||
|
sale: 2,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 14,
|
||||||
|
name: 'Fimi X8 SE Black',
|
||||||
|
price: 567,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/26.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 63,
|
||||||
|
sale: 115,
|
||||||
|
location: 'Brooklyn',
|
||||||
|
),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 15,
|
||||||
|
name: 'SAMSUNG GALAXY S20 PLUS RAM 8/128GB',
|
||||||
|
price: 751,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/30.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 14,
|
||||||
|
sale: 17,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 16,
|
||||||
|
name:
|
||||||
|
'BARDI Smart Light Bulb Lamp Bohlam LED WIFI RGBWW 12W 12 watt Home IoT',
|
||||||
|
price: 8.6,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/58.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 354,
|
||||||
|
sale: 540,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 17,
|
||||||
|
name:
|
||||||
|
'KABEL DATA TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M',
|
||||||
|
price: 3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/35.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 636,
|
||||||
|
sale: 2087,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 18,
|
||||||
|
name:
|
||||||
|
'gopro hero 8 black garansi resmi TAM / go pro hero8 black / 8black',
|
||||||
|
price: 333,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/12.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 13,
|
||||||
|
sale: 33,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 19,
|
||||||
|
name:
|
||||||
|
'Samyang Noodle Wholesaler 1 Box (40 Pcs) Hot Spicy Chicken - Original Jan 21',
|
||||||
|
price: 26,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/68.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 4,
|
||||||
|
sale: 22,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
SearchProductModel(
|
||||||
|
id: 20,
|
||||||
|
name: 'SAMSUNG LED TV 32 Inch HD Digital - 32N4003',
|
||||||
|
price: 116,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/80.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 380,
|
||||||
|
sale: 866,
|
||||||
|
location: 'Brooklyn')
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class ShoppingCartModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late String image;
|
||||||
|
late double price;
|
||||||
|
late int qty;
|
||||||
|
|
||||||
|
ShoppingCartModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.image,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.qty});
|
||||||
|
|
||||||
|
void setQty(int i) {
|
||||||
|
if (i < 1) {
|
||||||
|
qty = 1;
|
||||||
|
} else {
|
||||||
|
qty = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ShoppingCartModel> shoppingCartData = [
|
||||||
|
ShoppingCartModel(
|
||||||
|
id: 1,
|
||||||
|
name:
|
||||||
|
'BARDI Smart Light Bulb Lamp Bohlam LED WIFI RGBWW 12W 12 watt Home IoT',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/58.jpg',
|
||||||
|
price: 8.6,
|
||||||
|
qty: 2,
|
||||||
|
),
|
||||||
|
ShoppingCartModel(
|
||||||
|
id: 2,
|
||||||
|
name:
|
||||||
|
'BARDI Smart UNIVERSAL IR REMOTE Wifi Wireless IoT For Home Automation',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/60.jpg',
|
||||||
|
price: 11.5,
|
||||||
|
qty: 1,
|
||||||
|
),
|
||||||
|
ShoppingCartModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'BARDI Smart PLUG WiFi Wireless Colokan - IoT Smart Home',
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/69.jpg',
|
||||||
|
price: 11.46,
|
||||||
|
qty: 1,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class WishlistModel {
|
||||||
|
late int id;
|
||||||
|
late String name;
|
||||||
|
late double price;
|
||||||
|
late String image;
|
||||||
|
late double rating;
|
||||||
|
late int review;
|
||||||
|
late int sale;
|
||||||
|
late int stock;
|
||||||
|
late String location;
|
||||||
|
|
||||||
|
WishlistModel(
|
||||||
|
{required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.price,
|
||||||
|
required this.image,
|
||||||
|
required this.rating,
|
||||||
|
required this.review,
|
||||||
|
required this.sale,
|
||||||
|
required this.stock,
|
||||||
|
required this.location});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WishlistModel> wishlistData = [
|
||||||
|
WishlistModel(
|
||||||
|
id: 1,
|
||||||
|
name: 'IWO 8 Smart Watch Apple iWatch Mirror For Android iPhone',
|
||||||
|
price: 62,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/46.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 42,
|
||||||
|
sale: 69,
|
||||||
|
stock: 5,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
WishlistModel(
|
||||||
|
id: 2,
|
||||||
|
name: 'Asus Rog Phone 3 Rogphone III Ram 12Gb 512Gb Snapdragon 865+ Plus',
|
||||||
|
price: 1152,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/48.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1,
|
||||||
|
sale: 2,
|
||||||
|
stock: 22,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
WishlistModel(
|
||||||
|
id: 3,
|
||||||
|
name: 'BARDI Smart PLUG WiFi Wireless Colokan - IoT Smart Home',
|
||||||
|
price: 11.46,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/69.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 1062,
|
||||||
|
sale: 4797,
|
||||||
|
stock: 0,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
WishlistModel(
|
||||||
|
id: 4,
|
||||||
|
name: 'ipad Pro 2020 11-inch 128GB Wi-Fi Only - Silver',
|
||||||
|
price: 866,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/49.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 22,
|
||||||
|
sale: 468,
|
||||||
|
stock: 25,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
WishlistModel(
|
||||||
|
id: 5,
|
||||||
|
name: 'Fimi X8 SE Black',
|
||||||
|
price: 567,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/26.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 63,
|
||||||
|
sale: 115,
|
||||||
|
stock: 0,
|
||||||
|
location: 'Brooklyn',
|
||||||
|
),
|
||||||
|
WishlistModel(
|
||||||
|
id: 6,
|
||||||
|
name:
|
||||||
|
'Xiaomi Air Purifier 3 Mijia OLED Touch Sterilization Air Ionizer - 3',
|
||||||
|
price: 139,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/33.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 275,
|
||||||
|
sale: 1055,
|
||||||
|
stock: 4,
|
||||||
|
location: 'Brooklyn'),
|
||||||
|
WishlistModel(
|
||||||
|
id: 7,
|
||||||
|
name: 'Delta Boots Import 8 Inch',
|
||||||
|
price: 18.3,
|
||||||
|
image: GLOBAL_URL + '/apps/ecommerce/product/25.jpg',
|
||||||
|
rating: 5,
|
||||||
|
review: 212,
|
||||||
|
sale: 735,
|
||||||
|
stock: 55,
|
||||||
|
location: 'Brooklyn')
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
|
class AboutPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_AboutPageState createState() => _AboutPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AboutPageState extends State<AboutPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
String _version = '1.0.0';
|
||||||
|
|
||||||
|
Future<void> _getSystemDevice() async {
|
||||||
|
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
setState(() {
|
||||||
|
_version = packageInfo.version;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
if (!kIsWeb) {
|
||||||
|
_getSystemDevice();
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'About',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: Container(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Center(child: Image.asset('assets/images/logo.png', height: 32)),
|
||||||
|
SizedBox(
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'App Version',
|
||||||
|
style: TextStyle(fontSize: 14, color: CHARCOAL),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_version,
|
||||||
|
style: TextStyle(fontSize: 14, color: CHARCOAL),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/account/account_information/edit_email.dart';
|
||||||
|
import 'package:mobdr/ui/account/account_information/edit_name.dart';
|
||||||
|
import 'package:mobdr/ui/account/account_information/edit_phone_number.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class AccountInformationPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_AccountInformationPageState createState() => _AccountInformationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountInformationPageState extends State<AccountInformationPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Account Information',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_createProfilePicture(),
|
||||||
|
SizedBox(height: 40),
|
||||||
|
Text(
|
||||||
|
'Name',
|
||||||
|
style: GlobalStyle.accountInformationLabel,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'Robert Steven',
|
||||||
|
style: GlobalStyle.accountInformationValue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => EditNamePage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit',
|
||||||
|
style: GlobalStyle.accountInformationEdit),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Email',
|
||||||
|
style: GlobalStyle.accountInformationLabel,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
_verifiedLabel()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'robert.steven@ijtechnology.net',
|
||||||
|
style: GlobalStyle.accountInformationValue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => EditEmailPage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit',
|
||||||
|
style: GlobalStyle.accountInformationEdit),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Phone Number',
|
||||||
|
style: GlobalStyle.accountInformationLabel,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
_verifiedLabel()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'0811888999',
|
||||||
|
style: GlobalStyle.accountInformationValue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => EditPhoneNumberPage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit',
|
||||||
|
style: GlobalStyle.accountInformationEdit),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProfilePicture() {
|
||||||
|
final double profilePictureSize = MediaQuery.of(context).size.width / 3;
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 40),
|
||||||
|
width: profilePictureSize,
|
||||||
|
height: profilePictureSize,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_showPopupUpdatePicture();
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
radius: (profilePictureSize),
|
||||||
|
child: Hero(
|
||||||
|
tag: 'profilePicture',
|
||||||
|
child: ClipOval(
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: profilePictureSize,
|
||||||
|
height: profilePictureSize,
|
||||||
|
url: GLOBAL_URL + '/user/avatar.png')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// create edit icon in the picture
|
||||||
|
Container(
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: 0, left: MediaQuery.of(context).size.width / 4),
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
elevation: 1,
|
||||||
|
child: Icon(Icons.edit, size: 12, color: CHARCOAL),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _verifiedLabel() {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE, borderRadius: BorderRadius.circular(2)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('verified', style: TextStyle(color: Colors.white, fontSize: 11)),
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Icon(Icons.done, color: Colors.white, size: 11)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPopupUpdatePicture() {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Click edit profile picture',
|
||||||
|
toastLength: Toast.LENGTH_SHORT);
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Edit Profile Picture',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text('Do you want to edit profile picture ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
This is edit email page
|
||||||
|
|
||||||
|
include file in reuseable/global_function.dart to call function from GlobalFunction
|
||||||
|
include file in reuseable/global_widget.dart to call function from GlobalWidget
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EditEmailPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_EditEmailPageState createState() => _EditEmailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditEmailPageState extends State<EditEmailPage> {
|
||||||
|
// initialize reusable widget and global function
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
|
||||||
|
TextEditingController _etEmail = TextEditingController();
|
||||||
|
bool _buttonDisabled = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etEmail = TextEditingController(text: 'robert.steven@ijtechnology.net');
|
||||||
|
|
||||||
|
if (_globalFunction.validateEmail(_etEmail.text)) {
|
||||||
|
_buttonDisabled = false;
|
||||||
|
} else {
|
||||||
|
_buttonDisabled = true;
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etEmail.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Edit Email',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
controller: _etEmail,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {
|
||||||
|
if (_globalFunction.validateEmail(textValue)) {
|
||||||
|
_buttonDisabled = false;
|
||||||
|
} else {
|
||||||
|
_buttonDisabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Email',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) =>
|
||||||
|
states.contains(MaterialState.disabled)
|
||||||
|
? Colors.grey[300]!
|
||||||
|
: _buttonDisabled
|
||||||
|
? Colors.grey[300]!
|
||||||
|
: PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
if (!_buttonDisabled) {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Edit Email Success', 1);
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: _buttonDisabled
|
||||||
|
? Colors.grey[600]
|
||||||
|
: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EditNamePage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_EditNamePageState createState() => _EditNamePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditNamePageState extends State<EditNamePage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
TextEditingController _etName = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etName = TextEditingController(text: 'Robert Steven');
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etName.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Edit Name',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _etName,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Name',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Edit Name Success', 1);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EditPhoneNumberPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_EditPhoneNumberPageState createState() => _EditPhoneNumberPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditPhoneNumberPageState extends State<EditPhoneNumberPage> {
|
||||||
|
// initialize reusable widget and global function
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
|
||||||
|
TextEditingController _etPhoneNumber = TextEditingController();
|
||||||
|
bool _buttonDisabled = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etPhoneNumber = TextEditingController(text: '0811888999');
|
||||||
|
|
||||||
|
if (_globalFunction.validateMobileNumber(_etPhoneNumber.text)) {
|
||||||
|
_buttonDisabled = false;
|
||||||
|
} else {
|
||||||
|
_buttonDisabled = true;
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etPhoneNumber.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Edit Phone Number',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
controller: _etPhoneNumber,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {
|
||||||
|
if (_globalFunction.validateMobileNumber(textValue)) {
|
||||||
|
_buttonDisabled = false;
|
||||||
|
} else {
|
||||||
|
_buttonDisabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Phone Number',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) =>
|
||||||
|
states.contains(MaterialState.disabled)
|
||||||
|
? Colors.grey[300]!
|
||||||
|
: _buttonDisabled
|
||||||
|
? Colors.grey[300]!
|
||||||
|
: PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
if (!_buttonDisabled) {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Edit Phone Number Success', 1);
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: _buttonDisabled
|
||||||
|
? Colors.grey[600]
|
||||||
|
: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/last_seen_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LastSeenProductPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_LastSeenProductPageState createState() => _LastSeenProductPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LastSeenProductPageState extends State<LastSeenProductPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 4);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Last Seen Product',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: lastSeenData.length,
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 0),
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildItem(index, boxImageSize);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(index, boxImageSize) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: lastSeenData[index].name,
|
||||||
|
image: lastSeenData[index].image,
|
||||||
|
price: lastSeenData[index].price,
|
||||||
|
rating: lastSeenData[index].rating,
|
||||||
|
review: lastSeenData[index].review,
|
||||||
|
sale: lastSeenData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 6, 12, 6),
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: lastSeenData[index].image)),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
lastSeenData[index].name,
|
||||||
|
style: GlobalStyle.productName.copyWith(fontSize: 13),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
lastSeenData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.location_on,
|
||||||
|
color: SOFT_GREY, size: 12),
|
||||||
|
Text(' ' + lastSeenData[index].location,
|
||||||
|
style: GlobalStyle.productLocation)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: lastSeenData[index].rating, size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
lastSeenData[index].review.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
lastSeenData[index].sale.toString() + ' Sale',
|
||||||
|
style: GlobalStyle.productSale),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(index == lastSeenData.length - 1)
|
||||||
|
? Wrap()
|
||||||
|
: Divider(
|
||||||
|
height: 0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NotificationSettingPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_NotificationSettingPageState createState() =>
|
||||||
|
_NotificationSettingPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NotificationSettingPageState extends State<NotificationSettingPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
bool _valChat = true;
|
||||||
|
bool _valPromotion = true;
|
||||||
|
bool _valOrderStatus = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Notification Setting',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
_buildSwitchPromotion(),
|
||||||
|
Divider(height: 0, color: Colors.grey[400]),
|
||||||
|
_buildSwitchChat(),
|
||||||
|
Divider(height: 0, color: Colors.grey[400]),
|
||||||
|
_buildSwitchOrderStatus(),
|
||||||
|
Divider(height: 0, color: Colors.grey[400]),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSwitchPromotion() {
|
||||||
|
return SwitchListTile(
|
||||||
|
contentPadding: EdgeInsets.only(left: 16, right: 4),
|
||||||
|
title: Text(
|
||||||
|
'Promotion',
|
||||||
|
style: TextStyle(fontSize: 15, color: CHARCOAL),
|
||||||
|
),
|
||||||
|
value: _valPromotion,
|
||||||
|
activeColor: PRIMARY_COLOR,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
_valPromotion = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSwitchChat() {
|
||||||
|
return SwitchListTile(
|
||||||
|
contentPadding: EdgeInsets.only(left: 16, right: 4),
|
||||||
|
title: Text(
|
||||||
|
'Chat',
|
||||||
|
style: TextStyle(fontSize: 15, color: CHARCOAL),
|
||||||
|
),
|
||||||
|
value: _valChat,
|
||||||
|
activeColor: PRIMARY_COLOR,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
_valChat = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSwitchOrderStatus() {
|
||||||
|
return SwitchListTile(
|
||||||
|
contentPadding: EdgeInsets.only(left: 16, right: 4),
|
||||||
|
title: Text(
|
||||||
|
'New Order Status',
|
||||||
|
style: TextStyle(fontSize: 15, color: CHARCOAL),
|
||||||
|
),
|
||||||
|
value: _valOrderStatus,
|
||||||
|
activeColor: PRIMARY_COLOR,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
_valOrderStatus = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,484 @@
|
||||||
|
// ignore_for_file: prefer_const_constructors, prefer_interpolation_to_compose_strings, avoid_unnecessary_containers, use_key_in_widget_constructors
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/account/order/order_status.dart';
|
||||||
|
import 'package:mobdr/ui/general/chat_us.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OrderDetailPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_OrderDetailPageState createState() => _OrderDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderDetailPageState extends State<OrderDetailPage> {
|
||||||
|
// initialize global function
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 6);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
title: Text(
|
||||||
|
'Order Detail',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
_createOrderStatus(),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 6),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildItemCard(boxImageSize, 1),
|
||||||
|
_buildItemCard(boxImageSize, 2),
|
||||||
|
_buildItemCard(boxImageSize, 3),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_createDeliveryDetail(),
|
||||||
|
_createPaymentInformation(),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(32),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ChatUsPage()));
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||||
|
child: Text(
|
||||||
|
'Chat Us',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createOrderStatus() {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey,
|
||||||
|
offset: Offset(0.0, 1.0), //(x,y)
|
||||||
|
blurRadius: 3.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Status', style: TextStyle(color: BLACK_GREY, fontSize: 13)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Order Completed',
|
||||||
|
style: TextStyle(
|
||||||
|
color: PRIMARY_COLOR,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => OrderStatusPage()));
|
||||||
|
},
|
||||||
|
child: Text('View Status',
|
||||||
|
style: TextStyle(color: PRIMARY_COLOR, fontSize: 13)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Order Date',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 13)),
|
||||||
|
Text('3 September 2019 11:32 UTC',
|
||||||
|
style: TextStyle(
|
||||||
|
color: BLACK_GREY,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('INV385714262',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 13)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItemCard(boxImageSize, id) {
|
||||||
|
// create local data
|
||||||
|
String name = 'Delta Boots Import 8 Inch';
|
||||||
|
String image = GLOBAL_URL + '/product/25.jpg';
|
||||||
|
int countItem = 1;
|
||||||
|
int weight = 800;
|
||||||
|
double price = 18.3;
|
||||||
|
double totalPrice = 18.3;
|
||||||
|
if (id == 2) {
|
||||||
|
name =
|
||||||
|
'DATA CABLE TYPE-C TO TYPE-C BASEUS HALO DATA CABLE PD 2.0 60W - 0.5 M';
|
||||||
|
image = GLOBAL_URL + '/product/35.jpg';
|
||||||
|
countItem = 2;
|
||||||
|
weight = 100;
|
||||||
|
price = 3;
|
||||||
|
totalPrice = 6;
|
||||||
|
} else if (id == 3) {
|
||||||
|
name = 'TEROPONG MINI 30 X 60 BINOCULARS HD NIGHT VERSION 30 X 60';
|
||||||
|
image = GLOBAL_URL + '/product/2.jpg';
|
||||||
|
countItem = 1;
|
||||||
|
weight = 400;
|
||||||
|
price = 7.2;
|
||||||
|
totalPrice = 7.2;
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 6, 12, 0),
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: name,
|
||||||
|
image: image,
|
||||||
|
price: price,
|
||||||
|
rating: 5,
|
||||||
|
review: 23,
|
||||||
|
sale: 40)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: image)),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: GlobalStyle.productName.copyWith(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
countItem.toString() +
|
||||||
|
' item (' +
|
||||||
|
weight.toString() +
|
||||||
|
' gr)',
|
||||||
|
style: GlobalStyle.shoppingCartOtherProduct
|
||||||
|
.copyWith(color: Colors.grey[400]))),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
'\$' +
|
||||||
|
_globalFunction
|
||||||
|
.removeDecimalZeroFormat(price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Total Price', style: TextStyle(fontSize: 12)),
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
'\$' +
|
||||||
|
_globalFunction
|
||||||
|
.removeDecimalZeroFormat(totalPrice),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createDeliveryDetail() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey,
|
||||||
|
offset: Offset(0.0, 1.0), //(x,y)
|
||||||
|
blurRadius: 3.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Delivery Details',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Table(
|
||||||
|
children: [
|
||||||
|
TableRow(children: [
|
||||||
|
Container(
|
||||||
|
child: Text('Courier Delivery',
|
||||||
|
style: TextStyle(
|
||||||
|
color: BLACK_GREY,
|
||||||
|
fontSize: 13,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('DHL Express',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Text('AWB Number',
|
||||||
|
style: TextStyle(
|
||||||
|
color: BLACK_GREY,
|
||||||
|
fontSize: 13,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Text('5614571226',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Text('Delivery Address',
|
||||||
|
style: TextStyle(
|
||||||
|
color: BLACK_GREY,
|
||||||
|
fontSize: 13,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: Text('Robert Steven',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('0811888999',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('6019 Madison St',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('West New York, NJ 07093',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('USA',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createPaymentInformation() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey,
|
||||||
|
offset: Offset(0.0, 1.0), //(x,y)
|
||||||
|
blurRadius: 3.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Payment Information',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Payment Method',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 13)),
|
||||||
|
Text('Visa card ending in 4392',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Total Price (4 item)',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 13)),
|
||||||
|
Text('\$31.5', style: GlobalStyle.productPrice)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Total Delivery (1.3 Kg)',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 13)),
|
||||||
|
Text('\$19', style: GlobalStyle.productPrice)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Total Payment',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
Text('\$50.5', style: GlobalStyle.productPrice)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/order_list_model.dart';
|
||||||
|
import 'package:mobdr/ui/account/order/order_detail.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OrderListPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_OrderListPageState createState() => _OrderListPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderListPageState extends State<OrderListPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 6);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Order List',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: orderListData.length,
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 0),
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildOrderListCard(index, boxImageSize);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOrderListCard(index, boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 6, 12, 0),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => OrderDetailPage()));
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
child: Text(orderListData[index].status,
|
||||||
|
style: TextStyle(color: SOFT_BLUE, fontSize: 12)),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 12, 12, 0),
|
||||||
|
child: Text(orderListData[index].date,
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.grey[400])),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 8, 12, 0),
|
||||||
|
child: Text(orderListData[index].invoice,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: orderListData[index].image)),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
orderListData[index].name,
|
||||||
|
style: GlobalStyle.productName.copyWith(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('+2 other product',
|
||||||
|
style: GlobalStyle.shoppingCartOtherProduct
|
||||||
|
.copyWith(color: Colors.grey[400])))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(12),
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text('Total Payment', style: TextStyle(fontSize: 12)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
'\$' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
orderListData[index].payment),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OrderStatusPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_OrderStatusPageState createState() => _OrderStatusPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderStatusPageState extends State<OrderStatusPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Order Status',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: Container(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(32),
|
||||||
|
children: [
|
||||||
|
_createPathTop('11 Sep 2019 08:40', 'Order Completed',
|
||||||
|
'Your order is completed'),
|
||||||
|
_createPath('11 Sep 2019 08:39', 'Order Arrived',
|
||||||
|
'Your order has arrived'),
|
||||||
|
_createPath('9 Sep 2019 14:12', 'Order Sent',
|
||||||
|
'Your order is being shipped by courier'),
|
||||||
|
_createPath('9 Sep 2019 14:12', 'Ready to Pickup',
|
||||||
|
'Your order is ready to be picked up by the courier'),
|
||||||
|
_createPath('9 Sep 2019 12:12', 'Order Processed',
|
||||||
|
'Your order is being processed'),
|
||||||
|
_createPath('9 Sep 2019 11:52', 'Payment Received',
|
||||||
|
'Payment has been received'),
|
||||||
|
_createPath('9 Sep 2019 11:32', 'Waiting for Payment',
|
||||||
|
'We are waiting for your payment'),
|
||||||
|
_createPathDown('9 Sep 2019 11:32', 'Order Placed',
|
||||||
|
'We have received your order'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createPathTop(
|
||||||
|
String date, String orderStatus, String orderDescription) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 0),
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
decoration: new BoxDecoration(
|
||||||
|
shape: BoxShape
|
||||||
|
.circle, // You can use like this way or like the below line
|
||||||
|
//borderRadius: new BorderRadius.circular(30.0),
|
||||||
|
color: PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 7.5, right: 7.5),
|
||||||
|
child: Container(
|
||||||
|
width: 1,
|
||||||
|
color: PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(left: 32, right: 32),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(orderStatus,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(date,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.grey[400], fontSize: 11)),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(orderDescription,
|
||||||
|
style: TextStyle(color: BLACK_GREY)),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createPath(String date, String orderStatus, String orderDescription) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 0),
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
decoration: new BoxDecoration(
|
||||||
|
shape: BoxShape
|
||||||
|
.circle, // You can use like this way or like the below line
|
||||||
|
//borderRadius: new BorderRadius.circular(30.0),
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 7.5, right: 7.5),
|
||||||
|
child: Container(
|
||||||
|
width: 1,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(left: 32, right: 32),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(orderStatus,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(date,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.grey[400], fontSize: 11)),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(orderDescription,
|
||||||
|
style: TextStyle(color: BLACK_GREY)),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createPathDown(
|
||||||
|
String date, String orderStatus, String orderDescription) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 0),
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
decoration: new BoxDecoration(
|
||||||
|
shape: BoxShape
|
||||||
|
.circle, // You can use like this way or like the below line
|
||||||
|
//borderRadius: new BorderRadius.circular(30.0),
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(left: 48, right: 48),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(orderStatus,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(date,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.grey[400], fontSize: 11)),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(orderDescription,
|
||||||
|
style: TextStyle(color: BLACK_GREY)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,351 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AddPaymentMethodPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_AddPaymentMethodPageState createState() => _AddPaymentMethodPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddPaymentMethodPageState extends State<AddPaymentMethodPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
List<String> _monthList = [];
|
||||||
|
List<String> _yearList = [];
|
||||||
|
String? _expiredMonth;
|
||||||
|
String? _expiredYear;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
_initForLang();
|
||||||
|
});
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initForLang() {
|
||||||
|
setState(() {
|
||||||
|
_expiredMonth = 'Month';
|
||||||
|
_expiredYear = 'Year';
|
||||||
|
// select box data for month of credit card
|
||||||
|
_monthList.add('Month');
|
||||||
|
_monthList.add("01");
|
||||||
|
_monthList.add('02');
|
||||||
|
_monthList.add('03');
|
||||||
|
_monthList.add('04');
|
||||||
|
_monthList.add('05');
|
||||||
|
_monthList.add('06');
|
||||||
|
_monthList.add('07');
|
||||||
|
_monthList.add('08');
|
||||||
|
_monthList.add('09');
|
||||||
|
_monthList.add('10');
|
||||||
|
_monthList.add('11');
|
||||||
|
_monthList.add('12');
|
||||||
|
|
||||||
|
// select box data for year of credit card
|
||||||
|
_yearList.add('Year');
|
||||||
|
_yearList.add("2020");
|
||||||
|
_yearList.add('2021');
|
||||||
|
_yearList.add('2022');
|
||||||
|
_yearList.add('2023');
|
||||||
|
_yearList.add('2024');
|
||||||
|
_yearList.add('2025');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Add Payment Method',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Text('Credit Card Information',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/visa.png', height: 10),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
Image.asset('assets/images/mastercard.png', height: 20),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Credit Card Number *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Name of Cardholder *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Text('Expired Date *',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 12)),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_buildExpiredMonth(),
|
||||||
|
SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
_buildExpiredYear(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
Text('Billing Information',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Full Name *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Company Name',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 1 *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 2',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'City *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'State / Province / Region *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Postal Code *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Recipient\'s Phone Number *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Add Payment Method Success', 1);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropdown menu
|
||||||
|
DropdownButton<String> _buildExpiredMonth() {
|
||||||
|
return DropdownButton<String>(
|
||||||
|
value: _expiredMonth,
|
||||||
|
icon: Icon(Icons.arrow_drop_down),
|
||||||
|
iconSize: 24,
|
||||||
|
elevation: 16,
|
||||||
|
style: TextStyle(color: Colors.grey[700], fontSize: 16),
|
||||||
|
underline: Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
onChanged: (String? data) {
|
||||||
|
setState(() {
|
||||||
|
_expiredMonth = data!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
items: _monthList.map<DropdownMenuItem<String>>((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Container(
|
||||||
|
child: Text(value),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownButton<String> _buildExpiredYear() {
|
||||||
|
return DropdownButton<String>(
|
||||||
|
value: _expiredYear,
|
||||||
|
icon: Icon(Icons.arrow_drop_down),
|
||||||
|
iconSize: 24,
|
||||||
|
elevation: 16,
|
||||||
|
style: TextStyle(color: Colors.grey[700], fontSize: 16),
|
||||||
|
underline: Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
onChanged: (String? data) {
|
||||||
|
setState(() {
|
||||||
|
_expiredYear = data!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
items: _yearList.map<DropdownMenuItem<String>>((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Container(
|
||||||
|
child: Text(value),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,381 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EditPaymentMethodPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_EditPaymentMethodPageState createState() => _EditPaymentMethodPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditPaymentMethodPageState extends State<EditPaymentMethodPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
TextEditingController _etCreditCardNumber = TextEditingController();
|
||||||
|
TextEditingController _etCreditCardName = TextEditingController();
|
||||||
|
TextEditingController _etFullName = TextEditingController();
|
||||||
|
TextEditingController _etAddressLine1 = TextEditingController();
|
||||||
|
TextEditingController _etAddressLine2 = TextEditingController();
|
||||||
|
TextEditingController _etCity = TextEditingController();
|
||||||
|
TextEditingController _etState = TextEditingController();
|
||||||
|
TextEditingController _etPostalCode = TextEditingController();
|
||||||
|
TextEditingController _etPhoneNumber = TextEditingController();
|
||||||
|
|
||||||
|
List<String> _monthList = [];
|
||||||
|
List<String> _yearList = [];
|
||||||
|
String _expiredMonth = "04";
|
||||||
|
String _expiredYear = "2023";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etCreditCardNumber = TextEditingController(text: '4485653755194392');
|
||||||
|
_etCreditCardName = TextEditingController(text: 'Robert Steven');
|
||||||
|
_etFullName = TextEditingController(text: 'Robert Steven');
|
||||||
|
_etAddressLine1 = TextEditingController(
|
||||||
|
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.');
|
||||||
|
_etAddressLine2 = TextEditingController(
|
||||||
|
text:
|
||||||
|
'Quisque tortor tortor, ultrices id scelerisque a, elementum id elit.');
|
||||||
|
_etCity = TextEditingController(text: 'New York City');
|
||||||
|
_etState = TextEditingController(text: 'New York');
|
||||||
|
_etPostalCode = TextEditingController(text: '10010');
|
||||||
|
_etPhoneNumber = TextEditingController(text: '0811888999');
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
_initForLang();
|
||||||
|
});
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initForLang() {
|
||||||
|
setState(() {
|
||||||
|
// select box data for month of credit card
|
||||||
|
_monthList.add('Month');
|
||||||
|
_monthList.add("01");
|
||||||
|
_monthList.add('02');
|
||||||
|
_monthList.add('03');
|
||||||
|
_monthList.add('04');
|
||||||
|
_monthList.add('05');
|
||||||
|
_monthList.add('06');
|
||||||
|
_monthList.add('07');
|
||||||
|
_monthList.add('08');
|
||||||
|
_monthList.add('09');
|
||||||
|
_monthList.add('10');
|
||||||
|
_monthList.add('11');
|
||||||
|
_monthList.add('12');
|
||||||
|
|
||||||
|
// select box data for year of credit card
|
||||||
|
_yearList.add('Year');
|
||||||
|
_yearList.add("2020");
|
||||||
|
_yearList.add('2021');
|
||||||
|
_yearList.add('2022');
|
||||||
|
_yearList.add('2023');
|
||||||
|
_yearList.add('2024');
|
||||||
|
_yearList.add('2025');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Edit Payment Method',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Text('Credit Card Information',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/visa.png', height: 10),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
Image.asset('assets/images/mastercard.png', height: 20),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etCreditCardNumber,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Credit Card Number *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etCreditCardName,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Name of Cardholder *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Text('Expired Date *',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 12)),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_buildExpiredMonth(),
|
||||||
|
SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
_buildExpiredYear(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
Text('Billing Information',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
|
||||||
|
TextField(
|
||||||
|
controller: _etFullName,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Full Name *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Company Name',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etAddressLine1,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 1 *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etAddressLine2,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 2',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etCity,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'City *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etState,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'State / Province / Region *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etPostalCode,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Postal Code *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etPhoneNumber,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Phone Number *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Edit Payment Method Success', 1);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropdown menu
|
||||||
|
DropdownButton<String> _buildExpiredMonth() {
|
||||||
|
return DropdownButton<String>(
|
||||||
|
value: _expiredMonth,
|
||||||
|
icon: Icon(Icons.arrow_drop_down),
|
||||||
|
iconSize: 24,
|
||||||
|
elevation: 16,
|
||||||
|
style: TextStyle(color: Colors.grey[700], fontSize: 16),
|
||||||
|
underline: Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
onChanged: (String? data) {
|
||||||
|
setState(() {
|
||||||
|
_expiredMonth = data!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
items: _monthList.map<DropdownMenuItem<String>>((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Container(
|
||||||
|
child: Text(value),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownButton<String> _buildExpiredYear() {
|
||||||
|
return DropdownButton<String>(
|
||||||
|
value: _expiredYear,
|
||||||
|
icon: Icon(Icons.arrow_drop_down),
|
||||||
|
iconSize: 24,
|
||||||
|
elevation: 16,
|
||||||
|
style: TextStyle(color: Colors.grey[700], fontSize: 16),
|
||||||
|
underline: Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
onChanged: (String? data) {
|
||||||
|
setState(() {
|
||||||
|
_expiredYear = data!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
items: _yearList.map<DropdownMenuItem<String>>((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Container(
|
||||||
|
child: Text(value),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,470 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/account/payment_method/add_payment_method.dart';
|
||||||
|
import 'package:mobdr/ui/account/payment_method/edit_payment_method.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class PaymentMethodPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_PaymentMethodPageState createState() => _PaymentMethodPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaymentMethodPageState extends State<PaymentMethodPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Payment Method',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Text('Default Payment Preferences',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: CHARCOAL)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: Text('Payment Method',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14, color: Colors.grey[400])),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
Image.asset('assets/images/visa.png', height: 10),
|
||||||
|
),
|
||||||
|
Text('Visa card ending in 4392',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: CHARCOAL))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 24),
|
||||||
|
child: Text('Billing Address',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14, color: Colors.grey[400])),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit.',
|
||||||
|
style: TextStyle(fontSize: 14, color: CHARCOAL)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 24),
|
||||||
|
child: Text('Phone Number',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14, color: Colors.grey[400])),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Text('0811888999',
|
||||||
|
style: TextStyle(fontSize: 14, color: CHARCOAL)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 8),
|
||||||
|
child: Text('List of Payment Method',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: CHARCOAL)),
|
||||||
|
),
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: DataTable(
|
||||||
|
columns: <DataColumn>[
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Credit Card',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Name on Card',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Expires on',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Action',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL))),
|
||||||
|
],
|
||||||
|
rows: <DataRow>[
|
||||||
|
DataRow(
|
||||||
|
cells: <DataCell>[
|
||||||
|
DataCell(Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/visa.png',
|
||||||
|
height: 10),
|
||||||
|
),
|
||||||
|
Text('Visa card ending in 4392')
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
DataCell(Text("Robert Steven")),
|
||||||
|
DataCell(Text("04/2023")),
|
||||||
|
DataCell(Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
borderRadius: BorderRadius.circular(2)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('Default',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white, fontSize: 11)),
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Icon(Icons.done,
|
||||||
|
color: Colors.white, size: 11)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
EditPaymentMethodPage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 8),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_showPopupDeletePayment(0);
|
||||||
|
},
|
||||||
|
child: Text('Delete',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
DataRow(
|
||||||
|
cells: <DataCell>[
|
||||||
|
DataCell(Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/mastercard.png',
|
||||||
|
height: 20),
|
||||||
|
),
|
||||||
|
Text('MasterCard ending in 5309')
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
DataCell(Text("Robert Steven")),
|
||||||
|
DataCell(Text("07/2022")),
|
||||||
|
DataCell(Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showPopupMakeDefault();
|
||||||
|
},
|
||||||
|
child: Text('Make Default',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
EditPaymentMethodPage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 8),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_showPopupDeletePayment(1);
|
||||||
|
},
|
||||||
|
child: Text('Delete',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
DataRow(
|
||||||
|
cells: <DataCell>[
|
||||||
|
DataCell(Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/visa.png',
|
||||||
|
height: 10),
|
||||||
|
),
|
||||||
|
Text('Visa card ending in 2285')
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
DataCell(Text("Robert Steven")),
|
||||||
|
DataCell(Text("11/2021")),
|
||||||
|
DataCell(Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showPopupMakeDefault();
|
||||||
|
},
|
||||||
|
child: Text('Make Default',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
EditPaymentMethodPage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 8),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_showPopupDeletePayment(2);
|
||||||
|
},
|
||||||
|
child: Text('Delete',
|
||||||
|
style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 32),
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AddPaymentMethodPage()));
|
||||||
|
},
|
||||||
|
style: ButtonStyle(
|
||||||
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
side: MaterialStateProperty.all(
|
||||||
|
BorderSide(color: SOFT_BLUE, width: 1.0),
|
||||||
|
)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||||
|
child: Text(
|
||||||
|
'Add a card',
|
||||||
|
style: TextStyle(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 13),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void showPopupMakeDefault() {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
_reusableWidget.startLoading(context, 'Success', 0);
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Make Default',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text('Are you sure to make this card as a default payment ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPopupDeletePayment(int index) {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (index == 0) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg:
|
||||||
|
'Please change default payment method if you want to delete this payment method',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
} else {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Delete Payment Method Success', 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Delete Payment Method',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text('Are you sure to delete this payment method ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
|
||||||
|
class PrivacyPolicyPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_PrivacyPolicyPageState createState() => _PrivacyPolicyPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PrivacyPolicyPageState extends State<PrivacyPolicyPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Privacy Policy',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(textScaleFactor: 1),
|
||||||
|
child: Html(
|
||||||
|
data: """
|
||||||
|
<p><img src="$GLOBAL_URL/apps/ecommerce/account/privacy_policy.png"></p>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.</p>
|
||||||
|
<p>Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.</p>
|
||||||
|
<p>Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.</p>
|
||||||
|
<p>Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.</p>
|
||||||
|
<p>Phasellus pellentesque et magna in aliquam. Etiam vehicula dui vitae lectus ultrices iaculis. Nullam volutpat magna vel volutpat laoreet. Donec accumsan mi augue, nec elementum libero imperdiet eget. Duis in enim facilisis, lobortis tellus id, tincidunt urna. Donec ipsum neque, pharetra id imperdiet eget, varius bibendum sapien. Suspendisse tincidunt justo a purus molestie, sed elementum urna scelerisque. Suspendisse eget erat ultrices, suscipit nunc ut, iaculis lacus. Donec finibus, nisi vitae porta sodales, diam sapien scelerisque tortor, vel aliquet urna ex non urna. Etiam dictum eros ut justo ultrices tincidunt. Nulla et neque velit. Phasellus malesuada, lectus et sodales iaculis, sapien nibh ultrices tellus, ut ultrices magna tellus eget tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non diam ac quam aliquet facilisis sit amet at lectus. Nulla vestibulum libero arcu, eu malesuada ipsum congue feugiat.</p>
|
||||||
|
""",
|
||||||
|
style: {
|
||||||
|
"p": Style.fromTextStyle(
|
||||||
|
TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AddAddressPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_AddAddressPageState createState() => _AddAddressPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddAddressPageState extends State<AddAddressPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Add Address',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Title *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Recipient\'s Name *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Recipient\'s Phone Number',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 1 *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 2',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'State / Province / Region *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Postal Code *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Add Address Success', 1);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EditAddressPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_EditAddressPageState createState() => _EditAddressPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditAddressPageState extends State<EditAddressPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
// create controller to edit text field
|
||||||
|
TextEditingController _etAddressTitle = TextEditingController();
|
||||||
|
TextEditingController _etRecipientName = TextEditingController();
|
||||||
|
TextEditingController _etRecipientPhoneNumber = TextEditingController();
|
||||||
|
TextEditingController _etAddressLine1 = TextEditingController();
|
||||||
|
TextEditingController _etAddressLine2 = TextEditingController();
|
||||||
|
TextEditingController _etPostalCode = TextEditingController();
|
||||||
|
TextEditingController _etState = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etAddressTitle = TextEditingController(text: 'Home Address');
|
||||||
|
_etRecipientName = TextEditingController(text: 'Robert Steven');
|
||||||
|
_etRecipientPhoneNumber = TextEditingController(text: '0811888999');
|
||||||
|
_etAddressLine1 = TextEditingController(text: '6019 Madison St');
|
||||||
|
_etAddressLine2 = TextEditingController(text: 'West New York, NJ');
|
||||||
|
_etPostalCode = TextEditingController(text: '07093');
|
||||||
|
_etState = TextEditingController(text: 'USA');
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Edit Address',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _etAddressTitle,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Title *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etRecipientName,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Recipient\'s Name *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etRecipientPhoneNumber,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Recipient\'s Phone Number',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etAddressLine1,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 1 *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etAddressLine2,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Address Line 2',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etState,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'State / Province / Region *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _etPostalCode,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Postal Code *',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_reusableWidget.startLoading(
|
||||||
|
context, 'Edit Address Success', 1);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,260 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/address_model.dart';
|
||||||
|
import 'package:mobdr/ui/account/set_address/add_address.dart';
|
||||||
|
import 'package:mobdr/ui/account/set_address/edit_address.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class SetAddressPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_SetAddressPageState createState() => _SetAddressPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SetAddressPageState extends State<SetAddressPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Set Address',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(right: 20.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AddAddressPage()));
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 26.0,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: addressData.length,
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 0),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildAddressCard(index);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAddressCard(int index) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 6, 12, 0),
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// if default address == true, add default label
|
||||||
|
addressData[index].defaultAddress == true
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(addressData[index].title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, fontSize: 16)),
|
||||||
|
_reusableWidget.createDefaultLabel(context)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Text(addressData[index].title,
|
||||||
|
style: GlobalStyle.addressTitle),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20),
|
||||||
|
child: Text(addressData[index].recipientName,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(addressData[index].phoneNumber,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(addressData[index].addressLine1,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
addressData[index].addressLine2 +
|
||||||
|
' ' +
|
||||||
|
addressData[index].postalCode,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(addressData[index].state,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
addressData[index].defaultAddress == false
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_showPopupMakeDefault();
|
||||||
|
},
|
||||||
|
child: Text('Make Default',
|
||||||
|
style: GlobalStyle.addressAction),
|
||||||
|
)
|
||||||
|
: Wrap(),
|
||||||
|
index != 0
|
||||||
|
? SizedBox(
|
||||||
|
width: 12,
|
||||||
|
)
|
||||||
|
: Wrap(),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => EditAddressPage()));
|
||||||
|
},
|
||||||
|
child: Text('Edit', style: GlobalStyle.addressAction),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
_showPopupDeleteAddress(index);
|
||||||
|
},
|
||||||
|
child: Text('Delete', style: GlobalStyle.addressAction),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPopupDeleteAddress(int index) {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (index == 0) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg:
|
||||||
|
'Please change default address if you want to delete this address',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
addressData.removeAt(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Delete Address',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text('Are you sure to delete this address ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPopupMakeDefault() {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
_reusableWidget.startLoading(context, 'Success', 0);
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Make Default',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text('Are you sure to make this address as a default address ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
This is account page
|
||||||
|
we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/account/about.dart';
|
||||||
|
import 'package:mobdr/ui/account/account_information/account_information.dart';
|
||||||
|
import 'package:mobdr/ui/account/last_seen_product.dart';
|
||||||
|
import 'package:mobdr/ui/account/notification_setting.dart';
|
||||||
|
import 'package:mobdr/ui/account/order/order_list.dart';
|
||||||
|
import 'package:mobdr/ui/account/payment_method/payment_method.dart';
|
||||||
|
import 'package:mobdr/ui/account/privacy_policy.dart';
|
||||||
|
import 'package:mobdr/ui/account/set_address/set_address.dart';
|
||||||
|
import 'package:mobdr/ui/account/terms_conditions.dart';
|
||||||
|
import 'package:mobdr/ui/general/chat_us.dart';
|
||||||
|
import 'package:mobdr/ui/general/notification.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class TabAccountPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_TabAccountPageState createState() => _TabAccountPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TabAccountPageState extends State<TabAccountPage>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context);
|
||||||
|
super.build(context);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Account',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
actions: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => ChatUsPage()));
|
||||||
|
},
|
||||||
|
child: Icon(Icons.email, color: BLACK_GREY)),
|
||||||
|
IconButton(
|
||||||
|
icon: _reusableWidget.customNotifIcon(
|
||||||
|
count: 8, notifColor: BLACK_GREY),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => NotificationPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
_createAccountInformation(),
|
||||||
|
_createListMenu('Set Address for Delivery', SetAddressPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('Order List', OrderListPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('Payment Method', PaymentMethodPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('Last Seen Product', LastSeenProductPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('Notification Setting', NotificationSettingPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('About', AboutPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('Terms and Conditions', TermsConditionsPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
_createListMenu('Privacy Policy', PrivacyPolicyPage()),
|
||||||
|
_reusableWidget.divider1(),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(0, 18, 0, 0),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Click Sign Out', toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.power_settings_new,
|
||||||
|
size: 20, color: ASSENT_COLOR),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text('Sign Out',
|
||||||
|
style: TextStyle(fontSize: 15, color: ASSENT_COLOR)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createAccountInformation() {
|
||||||
|
final double profilePictureSize = MediaQuery.of(context).size.width / 4;
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 14),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: profilePictureSize,
|
||||||
|
height: profilePictureSize,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AccountInformationPage()));
|
||||||
|
},
|
||||||
|
child: CircleAvatar(
|
||||||
|
backgroundColor: Colors.grey[200],
|
||||||
|
radius: profilePictureSize,
|
||||||
|
child: CircleAvatar(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
radius: profilePictureSize - 4,
|
||||||
|
child: Hero(
|
||||||
|
tag: 'profilePicture',
|
||||||
|
child: ClipOval(
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: profilePictureSize - 4,
|
||||||
|
height: profilePictureSize - 4,
|
||||||
|
url: GLOBAL_URL + '/user/avatar.png')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Robert Steven',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AccountInformationPage()));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('Account Information',
|
||||||
|
style: TextStyle(fontSize: 14, color: BLACK_GREY)),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Icon(Icons.chevron_right, size: 20, color: SOFT_GREY)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createListMenu(String menuTitle, StatefulWidget page) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context, MaterialPageRoute(builder: (context) => page));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(0, 18, 0, 18),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(menuTitle, style: TextStyle(fontSize: 15, color: CHARCOAL)),
|
||||||
|
Icon(Icons.chevron_right, size: 20, color: SOFT_GREY),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
|
||||||
|
class TermsConditionsPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_TermsConditionsPageState createState() => _TermsConditionsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TermsConditionsPageState extends State<TermsConditionsPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Terms and Conditions',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(textScaleFactor: 1),
|
||||||
|
child: Html(
|
||||||
|
data: """
|
||||||
|
<p><img src="$GLOBAL_URL/apps/ecommerce/account/terms_conditions.png"></p>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.</p>
|
||||||
|
<p>Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.</p>
|
||||||
|
<p>Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.</p>
|
||||||
|
<p>Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.</p>
|
||||||
|
<p>Phasellus pellentesque et magna in aliquam. Etiam vehicula dui vitae lectus ultrices iaculis. Nullam volutpat magna vel volutpat laoreet. Donec accumsan mi augue, nec elementum libero imperdiet eget. Duis in enim facilisis, lobortis tellus id, tincidunt urna. Donec ipsum neque, pharetra id imperdiet eget, varius bibendum sapien. Suspendisse tincidunt justo a purus molestie, sed elementum urna scelerisque. Suspendisse eget erat ultrices, suscipit nunc ut, iaculis lacus. Donec finibus, nisi vitae porta sodales, diam sapien scelerisque tortor, vel aliquet urna ex non urna. Etiam dictum eros ut justo ultrices tincidunt. Nulla et neque velit. Phasellus malesuada, lectus et sodales iaculis, sapien nibh ultrices tellus, ut ultrices magna tellus eget tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non diam ac quam aliquet facilisis sit amet at lectus. Nulla vestibulum libero arcu, eu malesuada ipsum congue feugiat.</p>
|
||||||
|
""",
|
||||||
|
style: {
|
||||||
|
"p": Style.fromTextStyle(
|
||||||
|
TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
|
||||||
|
class ForgotPasswordPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_ForgotPasswordPageState createState() => _ForgotPasswordPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
|
||||||
|
TextEditingController _etEmail = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etEmail = TextEditingController(text: 'robert.steven@ijtechnology.net');
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etEmail.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.fromLTRB(30, 120, 30, 30),
|
||||||
|
children: <Widget>[
|
||||||
|
Center(child: Image.asset('assets/images/logo.png', height: 32)),
|
||||||
|
SizedBox(
|
||||||
|
height: 80,
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
controller: _etEmail,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Email',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'We will send the instruction to reset your password through the email',
|
||||||
|
style: GlobalStyle.resetPasswordNotes,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Click Reset Password',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Reset Password',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
|
// create sign in link
|
||||||
|
Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
GlobalStyle.iconBack,
|
||||||
|
Text(
|
||||||
|
' Back to login',
|
||||||
|
style: GlobalStyle.back,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/authentication/forgot_password.dart';
|
||||||
|
import 'package:mobdr/ui/home.dart';
|
||||||
|
import 'package:mobdr/ui/authentication/signup.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class SigninPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_SigninPageState createState() => _SigninPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SigninPageState extends State<SigninPage> {
|
||||||
|
TextEditingController _etEmail = TextEditingController();
|
||||||
|
bool _obscureText = true;
|
||||||
|
IconData _iconVisible = Icons.visibility_off;
|
||||||
|
|
||||||
|
void _toggleObscureText() {
|
||||||
|
setState(() {
|
||||||
|
_obscureText = !_obscureText;
|
||||||
|
if (_obscureText == true) {
|
||||||
|
_iconVisible = Icons.visibility_off;
|
||||||
|
} else {
|
||||||
|
_iconVisible = Icons.visibility;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etEmail.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.fromLTRB(30, 120, 30, 30),
|
||||||
|
children: <Widget>[
|
||||||
|
Center(child: Image.asset('assets/images/logo.png', height: 32)),
|
||||||
|
SizedBox(
|
||||||
|
height: 80,
|
||||||
|
),
|
||||||
|
Text('Sign In', style: GlobalStyle.authTitle),
|
||||||
|
TextFormField(
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
controller: _etEmail,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Email',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
obscureText: _obscureText,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Password',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(_iconVisible, color: Colors.grey[400], size: 20),
|
||||||
|
onPressed: () {
|
||||||
|
_toggleObscureText();
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Container(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ForgotPasswordPage()));
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Forgot Password?',
|
||||||
|
style: TextStyle(color: PRIMARY_COLOR, fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route<dynamic> route) => false);
|
||||||
|
Navigator.pushReplacement(context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage()));
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Login',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'Or sign in with',
|
||||||
|
style: GlobalStyle.authSignWith,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: <Widget>[
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route<dynamic> route) => false);
|
||||||
|
Navigator.pushReplacement(context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage()));
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Sign in with Google',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage("assets/images/google.png"),
|
||||||
|
width: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route<dynamic> route) => false);
|
||||||
|
Navigator.pushReplacement(context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage()));
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Sign in with Facebook',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage("assets/images/facebook.png"),
|
||||||
|
width: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route<dynamic> route) => false);
|
||||||
|
Navigator.pushReplacement(context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage()));
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Sign in with Twitter',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage("assets/images/twitter.png"),
|
||||||
|
width: 40,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => SignupPage()));
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
|
child: Wrap(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'No account yet? ',
|
||||||
|
style: GlobalStyle.authBottom1,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Create one',
|
||||||
|
style: GlobalStyle.authBottom2,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
GlobalStyle.iconBack,
|
||||||
|
Text(
|
||||||
|
' Back',
|
||||||
|
style: GlobalStyle.back,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
import 'package:mobdr/ui/home.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
|
||||||
|
class SignupPage extends StatefulWidget {
|
||||||
|
final bool fromList;
|
||||||
|
|
||||||
|
const SignupPage({Key? key, this.fromList = false}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SignupPageState createState() => _SignupPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SignupPageState extends State<SignupPage> {
|
||||||
|
TextEditingController _etEmail = TextEditingController();
|
||||||
|
TextEditingController _etName = TextEditingController();
|
||||||
|
bool _obscureText = true;
|
||||||
|
IconData _iconVisible = Icons.visibility_off;
|
||||||
|
|
||||||
|
void _toggleObscureText() {
|
||||||
|
setState(() {
|
||||||
|
_obscureText = !_obscureText;
|
||||||
|
if (_obscureText == true) {
|
||||||
|
_iconVisible = Icons.visibility_off;
|
||||||
|
} else {
|
||||||
|
_iconVisible = Icons.visibility;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etEmail.dispose();
|
||||||
|
_etName.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.fromLTRB(30, 120, 30, 30),
|
||||||
|
children: <Widget>[
|
||||||
|
Center(child: Image.asset('assets/images/logo.png', height: 32)),
|
||||||
|
SizedBox(
|
||||||
|
height: 80,
|
||||||
|
),
|
||||||
|
Text('Sign Up', style: GlobalStyle.authTitle),
|
||||||
|
TextFormField(
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
controller: _etEmail,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Email',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
controller: _etName,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Name',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
obscureText: _obscureText,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Color(0xFFCCCCCC)),
|
||||||
|
),
|
||||||
|
labelText: 'Password',
|
||||||
|
labelStyle: TextStyle(color: BLACK_GREY),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(_iconVisible, color: Colors.grey[400], size: 20),
|
||||||
|
onPressed: () {
|
||||||
|
_toggleObscureText();
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route<dynamic> route) => false);
|
||||||
|
if (!widget.fromList) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
Navigator.pushReplacement(context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage()));
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Register',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
GlobalStyle.iconBack,
|
||||||
|
Text(
|
||||||
|
' Back to login',
|
||||||
|
style: GlobalStyle.back,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,345 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/chat_model.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class ChatUsPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_ChatUsPageState createState() => _ChatUsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatUsPageState extends State<ChatUsPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
TextEditingController _etChat = TextEditingController();
|
||||||
|
|
||||||
|
String _lastDate = '13 Sep 2019';
|
||||||
|
|
||||||
|
List<ChatModel> _chatList = [];
|
||||||
|
List<ChatModel> _chatListReversed = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
_initForLang();
|
||||||
|
});
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initForLang() {
|
||||||
|
setState(() {
|
||||||
|
// set chat dummy data
|
||||||
|
_chatList.add(new ChatModel(1, null, 'date', '9 Sep 2019', null, null));
|
||||||
|
_chatList.add(
|
||||||
|
new ChatModel(2, 'buyer', 'text', 'Good morning', '13:59', true));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
3, 'buyer', 'image', GLOBAL_URL + '/product/80.jpg', null, null));
|
||||||
|
_chatList.add(new ChatModel(4, 'buyer', 'text',
|
||||||
|
'I want to ask about the samsung tv product', '13:59', true));
|
||||||
|
_chatList.add(new ChatModel(5, 'buyer', 'text',
|
||||||
|
'Is the Samsung LED TV 32 Inch is still on sale ?', '14:01', true));
|
||||||
|
_chatList.add(new ChatModel(6, 'seller', 'text',
|
||||||
|
'Hello, thank you for contacting us', '14:18', true));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
7,
|
||||||
|
'seller',
|
||||||
|
'text',
|
||||||
|
'We are sorry, but the promotion for Samsung LED TV 32 Inch has ended. Don\'t forget to turn on notification setting for promotion so you will get the news about our product',
|
||||||
|
'14:20',
|
||||||
|
null));
|
||||||
|
_chatList.add(new ChatModel(8, 'buyer', 'text',
|
||||||
|
'Ok, thank you for your information.', '14:22', true));
|
||||||
|
_chatList.add(new ChatModel(9, null, 'date', '13 Sep 2019', null, null));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
10, 'buyer', 'image', GLOBAL_URL + '/product/21.jpg', null, null));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
11,
|
||||||
|
'buyer',
|
||||||
|
'text',
|
||||||
|
'Hi, is Adidas Polo Shirt size L is ready ? For the black color.',
|
||||||
|
'08:58',
|
||||||
|
true));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
12, 'buyer', 'text', 'I want to order for 2 pcs.', '09:00', true));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
13,
|
||||||
|
'buyer',
|
||||||
|
'text',
|
||||||
|
'And can I change it if the size doesn\'t fit my body?',
|
||||||
|
'09:00',
|
||||||
|
true));
|
||||||
|
_chatList.add(new ChatModel(
|
||||||
|
14,
|
||||||
|
'seller',
|
||||||
|
'text',
|
||||||
|
'Hello, good morning. The product is ready and you can change if the size is not fit your body',
|
||||||
|
'09:14',
|
||||||
|
null));
|
||||||
|
|
||||||
|
// reverse the list
|
||||||
|
_chatListReversed = _chatList.reversed.toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etChat.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addDate(String currentDate) {
|
||||||
|
_chatListReversed.insert(
|
||||||
|
0, new ChatModel(15, null, 'date', currentDate, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addMessage(String message) {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
String _currentTime = DateFormat('kk:mm').format(now);
|
||||||
|
_chatListReversed.insert(
|
||||||
|
0, new ChatModel(16, 'buyer', 'text', message, _currentTime, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Chat',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: ListView.builder(
|
||||||
|
reverse: true,
|
||||||
|
itemCount: _chatListReversed.length,
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (_chatListReversed[index].getTextImageDate == 'date') {
|
||||||
|
return _buildDate(_chatListReversed[index].getMessage);
|
||||||
|
} else if (_chatListReversed[index].getTextImageDate ==
|
||||||
|
'image') {
|
||||||
|
return _buildImage(_chatListReversed[index].getMessage);
|
||||||
|
} else {
|
||||||
|
if (_chatListReversed[index].getType == 'buyer') {
|
||||||
|
return _buildChatBuyer(
|
||||||
|
_chatListReversed[index].getMessage,
|
||||||
|
_chatListReversed[index].getDate!,
|
||||||
|
_chatListReversed[index].getRead!);
|
||||||
|
} else {
|
||||||
|
return _buildChatSeller(
|
||||||
|
_chatListReversed[index].getMessage,
|
||||||
|
_chatListReversed[index].getDate!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _etChat,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 4,
|
||||||
|
textAlignVertical: TextAlignVertical.bottom,
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
fillColor: Colors.grey[200],
|
||||||
|
filled: true,
|
||||||
|
hintText: 'Write Message',
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
borderSide: BorderSide(color: Colors.grey[200]!)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
borderSide: BorderSide(color: Colors.grey[200]!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (_etChat.text != '') {
|
||||||
|
print('send message : ' + _etChat.text);
|
||||||
|
setState(() {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
String currentDate =
|
||||||
|
DateFormat('d MMM yyyy').format(now);
|
||||||
|
if (_lastDate != currentDate) {
|
||||||
|
_lastDate = currentDate;
|
||||||
|
_addDate(currentDate);
|
||||||
|
}
|
||||||
|
_addMessage(_etChat.text);
|
||||||
|
_etChat.text = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Icon(Icons.send, color: Colors.white)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDate(String date) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.all(16),
|
||||||
|
child: Center(
|
||||||
|
child: Text(date, style: TextStyle(color: SOFT_GREY, fontSize: 11)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildChatBuyer(String message, String time, bool read) {
|
||||||
|
final double boxChatSize = MediaQuery.of(context).size.width / 1.3;
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.end,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: boxChatSize),
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(width: 1, color: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(5),
|
||||||
|
bottomLeft: Radius.circular(5),
|
||||||
|
bottomRight: Radius.circular(12),
|
||||||
|
),
|
||||||
|
color: Colors.grey[300]),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(message, style: TextStyle(color: CHARCOAL)),
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Icon(Icons.done_all,
|
||||||
|
color: read == true ? PRIMARY_COLOR : SOFT_GREY,
|
||||||
|
size: 11),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
Text(time, style: TextStyle(color: SOFT_GREY, fontSize: 9)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildChatSeller(String message, String date) {
|
||||||
|
final double boxChatSize = MediaQuery.of(context).size.width / 1.3;
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Wrap(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: boxChatSize),
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(width: 1, color: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topRight: Radius.circular(5),
|
||||||
|
bottomLeft: Radius.circular(12),
|
||||||
|
bottomRight: Radius.circular(5),
|
||||||
|
)),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(message, style: TextStyle(color: CHARCOAL)),
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 2),
|
||||||
|
Text(date,
|
||||||
|
style: TextStyle(color: SOFT_GREY, fontSize: 9)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildImage(String imageUrl) {
|
||||||
|
final double boxChatSize = MediaQuery.of(context).size.width / 1.3;
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 6);
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.end,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: boxChatSize),
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(width: 1, color: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(5),
|
||||||
|
bottomLeft: Radius.circular(5),
|
||||||
|
bottomRight: Radius.circular(12),
|
||||||
|
)),
|
||||||
|
child: Container(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: imageUrl)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/account/order/order_status.dart';
|
||||||
|
import 'package:mobdr/ui/home/flashsale.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NotificationPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_NotificationPageState createState() => _NotificationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NotificationPageState extends State<NotificationPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Notification',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: ListView(children: <Widget>[
|
||||||
|
_createItem(
|
||||||
|
notifDate: '11 Sep 2019 08:40',
|
||||||
|
notifTitle: 'Order Completed',
|
||||||
|
notifMessage: 'Your order is completed',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '11 Sep 2019 08:39',
|
||||||
|
notifTitle: 'Order Arrived',
|
||||||
|
notifMessage: 'Your order has arrived',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '10 Sep 2019 10:00',
|
||||||
|
notifTitle: 'Flash Sale',
|
||||||
|
notifMessage:
|
||||||
|
'Hi Robert Steven, Flash Sale is open in 10 minutes. Grab your favorite product on sale',
|
||||||
|
page: FlashSalePage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 14:12',
|
||||||
|
notifTitle: 'Order Sent',
|
||||||
|
notifMessage: 'Your order is being shipped by courier',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 14:12',
|
||||||
|
notifTitle: 'Ready to Pickup',
|
||||||
|
notifMessage:
|
||||||
|
'Your order is ready to be picked up by the courier',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 13:00',
|
||||||
|
notifTitle: 'Trending Product',
|
||||||
|
notifMessage:
|
||||||
|
'Hi Robert Steven, there is a trending product for you, check it out now'),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 12:12',
|
||||||
|
notifTitle: 'Order Processed',
|
||||||
|
notifMessage: 'Your order is being processed',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 11:52',
|
||||||
|
notifTitle: 'Payment Received',
|
||||||
|
notifMessage: 'Payment has been received',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 11:32',
|
||||||
|
notifTitle: 'Waiting for Payment',
|
||||||
|
notifMessage: 'We are waiting for your payment',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
_createItem(
|
||||||
|
notifDate: '9 Sep 2019 11:32',
|
||||||
|
notifTitle: 'Order Placed',
|
||||||
|
notifMessage: 'We have received your order',
|
||||||
|
page: OrderStatusPage()),
|
||||||
|
])),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createItem(
|
||||||
|
{required String notifDate,
|
||||||
|
required String notifTitle,
|
||||||
|
required String notifMessage,
|
||||||
|
StatefulWidget? page}) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
if (page != null) {
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (context) => page));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(notifTitle,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: CHARCOAL)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text(notifDate,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.grey[400], fontSize: 11)),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(notifMessage, style: TextStyle(color: BLACK_GREY)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DeliveryEstimatedPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_DeliveryEstimatedPageState createState() => _DeliveryEstimatedPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DeliveryEstimatedPageState extends State<DeliveryEstimatedPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Delivery Estimated',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [_createLocationInformation(), _createCourierInformation()],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createLocationInformation() {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Location',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text('Delivery from :',
|
||||||
|
style: TextStyle(color: SOFT_GREY, fontSize: 14)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Text('Brooklyn, NY 11204, USA',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Text('Delivery to :',
|
||||||
|
style: TextStyle(color: SOFT_GREY, fontSize: 14)),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('Robert Steven',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('0811888999',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('6019 Madison St',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('West New York, NJ 07093',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('USA', style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createCourierInformation() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Courier', style: GlobalStyle.chooseCourier),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text('Courier price based on weight per 1000gr',
|
||||||
|
style: TextStyle(color: SOFT_GREY, fontSize: 14)),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('DHL', style: GlobalStyle.courierTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$13', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$22', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('FedEx', style: GlobalStyle.courierTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 1', style: GlobalStyle.courierTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 2', style: GlobalStyle.courierTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 3', style: GlobalStyle.courierTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 4', style: GlobalStyle.courierTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ProductDescriptionPage extends StatefulWidget {
|
||||||
|
final String name, image;
|
||||||
|
|
||||||
|
const ProductDescriptionPage({Key? key, this.name = '', this.image = ''})
|
||||||
|
: super(key: key);
|
||||||
|
@override
|
||||||
|
_ProductDescriptionPageState createState() => _ProductDescriptionPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductDescriptionPageState extends State<ProductDescriptionPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 4);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Product Description',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
_createProductImageAndTitle(boxImageSize),
|
||||||
|
Divider(height: 0, color: Colors.grey[400]),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Text('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales. Nulla at cursus eros. Integer porttitor ac ipsum quis sollicitudin. Sed mollis sapien massa, et dignissim turpis vulputate et. Ut ac odio porta, blandit velit in, pharetra lacus. Integer aliquam dolor nec augue porttitor hendrerit. Vestibulum aliquam urna finibus, luctus felis nec, hendrerit augue. Fusce eget lacinia leo. Vivamus porttitor, lacus eget rutrum tempus, odio magna tincidunt elit, ut vulputate nibh velit eu lectus. Morbi felis mi, efficitur sed diam in, elementum ullamcorper leo. Ut bibendum lorem consectetur pellentesque gravida. Sed est orci, consectetur id nunc quis, volutpat consectetur nisi.\n\n' +
|
||||||
|
'Donec est neque, accumsan sit amet imperdiet porta, suscipit eu ex. Phasellus lobortis mollis pharetra. Donec maximus rhoncus elit, sed pellentesque justo pretium vel. Integer vitae facilisis lectus. Suspendisse potenti. Mauris iaculis placerat feugiat. Integer commodo dui sit amet finibus congue. Nulla egestas lacus vel elit aliquet, at pulvinar ex venenatis. Vivamus eget maximus libero, quis vulputate diam. Pellentesque vel justo vel lectus viverra aliquet ut eget metus.\n\n' +
|
||||||
|
'Vivamus malesuada velit pretium laoreet pulvinar. Duis non dignissim sapien, vitae viverra purus. Curabitur a gravida mauris. Nullam turpis odio, ultricies sed ultricies non, sodales eget purus. Donec pulvinar bibendum metus vitae ornare. Phasellus eleifend orci eget blandit sollicitudin. Sed sed urna in magna dignissim eleifend.\n\n' +
|
||||||
|
'Vestibulum vitae erat maximus, laoreet ex quis, laoreet nunc. Sed porttitor massa eget cursus rhoncus. Suspendisse et tellus et enim ullamcorper semper eget in nisl. Nam metus mauris, sollicitudin in venenatis at, pretium at nulla. Sed a accumsan dui. Quisque fermentum mollis erat, ac fringilla eros auctor eu. Donec placerat mi ut sem ullamcorper tempor. Pellentesque ut nulla sollicitudin, tempus arcu quis, vulputate dolor. Sed ultrices cursus nisl, nec tempor neque tempus at. Pellentesque nec dolor faucibus, porttitor quam sed, vehicula est. Vestibulum placerat placerat neque eu posuere. Pellentesque id mauris hendrerit, placerat lacus id, auctor eros. Praesent vestibulum mattis est, non facilisis urna accumsan et. Vestibulum scelerisque ornare sapien, nec blandit purus rhoncus mollis. Sed faucibus, augue consequat rhoncus rutrum, sapien mauris dictum quam, nec tempus orci urna vitae lorem. Curabitur sit amet nisl et lacus fringilla pulvinar.'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductImageAndTitle(boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.all(24),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: widget.image)),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.name,
|
||||||
|
style: GlobalStyle.productName
|
||||||
|
.copyWith(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,818 @@
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/related_product_model.dart';
|
||||||
|
import 'package:mobdr/model/review_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/chat_us.dart';
|
||||||
|
import 'package:mobdr/ui/general/notification.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/delivery_estimated.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_description.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_review.dart';
|
||||||
|
import 'package:mobdr/ui/home/product_category.dart';
|
||||||
|
import 'package:mobdr/ui/home/search.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/shopping_cart/tab_shopping_cart.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class ProductDetailPage extends StatefulWidget {
|
||||||
|
final String name;
|
||||||
|
final String image;
|
||||||
|
final double price;
|
||||||
|
final double rating;
|
||||||
|
final int review;
|
||||||
|
final int sale;
|
||||||
|
|
||||||
|
const ProductDetailPage(
|
||||||
|
{Key? key,
|
||||||
|
this.name = '',
|
||||||
|
this.image = '',
|
||||||
|
this.price = 24,
|
||||||
|
this.rating = 4,
|
||||||
|
this.review = 45,
|
||||||
|
this.sale = 63})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ProductDetailPageState createState() => _ProductDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductDetailPageState extends State<ProductDetailPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
final List<String> _imgProductSlider = [];
|
||||||
|
int _currentImageSlider = 0;
|
||||||
|
|
||||||
|
// size data
|
||||||
|
List<String> _sizeList = ['XS', 'S', 'M', 'L', 'XL', 'XXL'];
|
||||||
|
int _sizeIndex = 0;
|
||||||
|
|
||||||
|
// color data
|
||||||
|
List<String> _colorList = [
|
||||||
|
'Red',
|
||||||
|
'Black',
|
||||||
|
'Green',
|
||||||
|
'White',
|
||||||
|
'Blue',
|
||||||
|
'Yellow',
|
||||||
|
'Pink'
|
||||||
|
];
|
||||||
|
int _colorIndex = 0;
|
||||||
|
|
||||||
|
// wishlist
|
||||||
|
bool _isLove = false;
|
||||||
|
|
||||||
|
// shopping cart count
|
||||||
|
int _shoppingCartCount = 3;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// image slider for the product
|
||||||
|
_imgProductSlider.add(widget.image);
|
||||||
|
_imgProductSlider.add(widget.image);
|
||||||
|
_imgProductSlider.add(widget.image);
|
||||||
|
_imgProductSlider.add(widget.image);
|
||||||
|
_imgProductSlider.add(widget.image);
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 3);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
titleSpacing: 0.0,
|
||||||
|
// create search text field in the app bar
|
||||||
|
title: Container(
|
||||||
|
margin: EdgeInsets.only(right: 16),
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => Colors.grey[100]!,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => SearchPage()));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Icon(Icons.search, color: Colors.grey[500], size: 18),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Search Product',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
icon: _customShoppingCart(_shoppingCartCount),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => TabShoppingCartPage()));
|
||||||
|
}),
|
||||||
|
IconButton(
|
||||||
|
icon: _reusableWidget.customNotifIcon(
|
||||||
|
count: 8, notifColor: BLACK_GREY),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => NotificationPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
_createProductSlider(),
|
||||||
|
_createProductPriceTitleEtc(),
|
||||||
|
_createProductVariant(),
|
||||||
|
_createDeliveryEstimated(),
|
||||||
|
_createProductInformation(),
|
||||||
|
_createProductDescription(),
|
||||||
|
_createProductRelated(boxImageSize),
|
||||||
|
_createProductReview(),
|
||||||
|
SizedBox(height: 16)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey,
|
||||||
|
offset: Offset(0.0, 1.0), //(x,y)
|
||||||
|
blurRadius: 2.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ChatUsPage()));
|
||||||
|
},
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
padding: EdgeInsets.all(9),
|
||||||
|
child: Icon(Icons.chat,
|
||||||
|
color: Colors.white, size: 16)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_shoppingCartCount++;
|
||||||
|
});
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Item has been added to Shopping Cart',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(width: 1, color: SOFT_BLUE),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(
|
||||||
|
10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Text('Add to Shopping Cart',
|
||||||
|
style: TextStyle(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _customShoppingCart(int count) {
|
||||||
|
return Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.shopping_cart, color: BLACK_GREY),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ASSENT_COLOR,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: 14,
|
||||||
|
minHeight: 14,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
count.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductSlider() {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
CarouselSlider(
|
||||||
|
items: _imgProductSlider
|
||||||
|
.map((item) => Container(
|
||||||
|
child:
|
||||||
|
buildCacheNetworkImage(width: 0, height: 0, url: item),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
options: CarouselOptions(
|
||||||
|
aspectRatio: 1,
|
||||||
|
viewportFraction: 1.0,
|
||||||
|
autoPlay: false,
|
||||||
|
enlargeCenterPage: false,
|
||||||
|
onPageChanged: (index, reason) {
|
||||||
|
setState(() {
|
||||||
|
_currentImageSlider = index;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 16,
|
||||||
|
left: 16,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE, borderRadius: BorderRadius.circular(4)),
|
||||||
|
child: Text(
|
||||||
|
(_currentImageSlider + 1).toString() +
|
||||||
|
'/' +
|
||||||
|
_imgProductSlider.length.toString(),
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 11)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductPriceTitleEtc() {
|
||||||
|
return Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('\$' + _globalFunction.removeDecimalZeroFormat(widget.price),
|
||||||
|
style: GlobalStyle.detailProductPrice),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
if (_isLove == true) {
|
||||||
|
_isLove = false;
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Item has been deleted from your wishlist',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
} else {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Item has been added to your wishlist',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
_isLove = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(Icons.favorite,
|
||||||
|
color: _isLove == true ? ASSENT_COLOR : BLACK_GREY,
|
||||||
|
size: 28),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Text(widget.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
)),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductReviewPage()));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.star, color: Colors.yellow[700], size: 18),
|
||||||
|
SizedBox(
|
||||||
|
width: 3,
|
||||||
|
),
|
||||||
|
Text(widget.rating.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, fontSize: 14)),
|
||||||
|
SizedBox(
|
||||||
|
width: 3,
|
||||||
|
),
|
||||||
|
Text('(' + widget.review.toString() + ')',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
VerticalDivider(
|
||||||
|
width: 30,
|
||||||
|
thickness: 1,
|
||||||
|
color: Colors.grey[300],
|
||||||
|
),
|
||||||
|
Text(widget.sale.toString() + ' Sale',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
VerticalDivider(
|
||||||
|
width: 30,
|
||||||
|
thickness: 1,
|
||||||
|
color: Colors.grey[300],
|
||||||
|
),
|
||||||
|
Icon(Icons.location_on, color: SOFT_GREY, size: 16),
|
||||||
|
Text('Brooklyn',
|
||||||
|
style: TextStyle(fontSize: 13, color: SOFT_GREY))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductVariant() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Variant', style: GlobalStyle.sectionTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text('Size : ',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 14)),
|
||||||
|
Text(_sizeList[_sizeIndex],
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
children: List.generate(_sizeList.length, (index) {
|
||||||
|
return radioSize(_sizeList[index], index);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text('Color : ',
|
||||||
|
style: TextStyle(color: BLACK_GREY, fontSize: 14)),
|
||||||
|
Text(_colorList[_colorIndex],
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
children: List.generate(_colorList.length, (index) {
|
||||||
|
return radioColor(_colorList[index], index);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget radioSize(String txt, int index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_sizeIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8, top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _sizeIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: _sizeIndex == index ? SOFT_BLUE : Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _sizeIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget radioColor(String txt, int index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_colorIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8, top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _colorIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: _colorIndex == index ? SOFT_BLUE : Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _colorIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createDeliveryEstimated() {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => DeliveryEstimatedPage()));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Delivery', style: GlobalStyle.sectionTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
RichText(
|
||||||
|
text: new TextSpan(
|
||||||
|
// Note: Styles for TextSpans must be explicitly defined.
|
||||||
|
// Child text spans will inherit styles from parent
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 15.5,
|
||||||
|
color: BLACK_GREY,
|
||||||
|
),
|
||||||
|
children: <TextSpan>[
|
||||||
|
new TextSpan(
|
||||||
|
text:
|
||||||
|
'Calculate the estimated cost for shipping goods to '),
|
||||||
|
new TextSpan(
|
||||||
|
text: 'West New York, NJ',
|
||||||
|
style:
|
||||||
|
new TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(Icons.chevron_right, size: 36, color: CHARCOAL)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductInformation() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Information', style: GlobalStyle.sectionTitle),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Weight', style: TextStyle(color: BLACK_GREY)),
|
||||||
|
Text('300 Gram', style: TextStyle(color: BLACK_GREY))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Condition', style: TextStyle(color: BLACK_GREY)),
|
||||||
|
Text('Second', style: TextStyle(color: BLACK_GREY))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Category', style: TextStyle(color: BLACK_GREY)),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: 3, categoryName: 'Electronic')));
|
||||||
|
},
|
||||||
|
child: Text('Electronic', style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductDescription() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Description',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nQuisque tortor tortor, ultrices id scelerisque a, elementum id elit. Maecenas feugiat tellus sed augue malesuada, id tempus ex sodales.'),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDescriptionPage(
|
||||||
|
name: widget.name, image: widget.image)));
|
||||||
|
},
|
||||||
|
child: Center(
|
||||||
|
child: Text('Read More', style: TextStyle(color: SOFT_BLUE)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductRelated(boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.only(bottom: 16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(16),
|
||||||
|
child: Text('Related Product', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: boxImageSize *
|
||||||
|
GlobalStyle.horizontalProductHeightMultiplication,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: relatedProductData.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return Container(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: relatedProductData[index].name,
|
||||||
|
image: relatedProductData[index].image,
|
||||||
|
price: relatedProductData[index].price,
|
||||||
|
rating: relatedProductData[index].rating,
|
||||||
|
review: relatedProductData[index].review,
|
||||||
|
sale: 36)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
height: boxImageSize + 10,
|
||||||
|
url: relatedProductData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
relatedProductData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction
|
||||||
|
.removeDecimalZeroFormat(
|
||||||
|
relatedProductData[index]
|
||||||
|
.price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: relatedProductData[index]
|
||||||
|
.rating,
|
||||||
|
size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
relatedProductData[index]
|
||||||
|
.review
|
||||||
|
.toString() +
|
||||||
|
')',
|
||||||
|
style:
|
||||||
|
GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createProductReview() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Review', style: GlobalStyle.sectionTitle),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductReviewPage()));
|
||||||
|
},
|
||||||
|
child: Text('View All',
|
||||||
|
style: GlobalStyle.viewAll.copyWith(color: SOFT_BLUE),
|
||||||
|
textAlign: TextAlign.end),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: widget.rating, size: 12),
|
||||||
|
Text('(' + widget.review.toString() + ')',
|
||||||
|
style: TextStyle(fontSize: 11, color: SOFT_GREY))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: List.generate(reviewData.length, (index) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(reviewData[index].date,
|
||||||
|
style: TextStyle(fontSize: 13, color: SOFT_GREY)),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(reviewData[index].name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: reviewData[index].rating, size: 12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
Text(reviewData[index].review)
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/review_model.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ProductReviewPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_ProductReviewPageState createState() => _ProductReviewPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductReviewPageState extends State<ProductReviewPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
List<String> starList = ['All Review', '1', '2', '3', '4', '5'];
|
||||||
|
int starIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Product Review',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: List.generate(starList.length, (index) {
|
||||||
|
return radioStar(starList[index], index);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Container(
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: ScrollPhysics(),
|
||||||
|
itemCount: reviewData.length,
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildReviewCard(index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget radioStar(String txt, int index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
starIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: starIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: starIndex == index ? SOFT_BLUE : Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
|
child: index == 0
|
||||||
|
? Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color: starIndex == index ? Colors.white : CHARCOAL))
|
||||||
|
: Wrap(
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
starIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
Icon(Icons.star,
|
||||||
|
color: starIndex == index
|
||||||
|
? Colors.white
|
||||||
|
: Colors.yellow[700],
|
||||||
|
size: 12),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildReviewCard(index) {
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(reviewData[index].date,
|
||||||
|
style: TextStyle(fontSize: 13, color: SOFT_GREY)),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(reviewData[index].name,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: reviewData[index].rating, size: 12)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
Text(reviewData[index].review)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
import 'package:mobdr/ui/account/tab_account.dart';
|
||||||
|
import 'package:mobdr/ui/home/tab_home.dart';
|
||||||
|
import 'package:mobdr/ui/shopping_cart/tab_shopping_cart.dart';
|
||||||
|
import 'package:mobdr/ui/wishlist/tab_wishlist.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
|
||||||
|
class HomePage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_HomePageState createState() => _HomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomePageState extends State<HomePage>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late PageController _pageController;
|
||||||
|
int _currentIndex = 0;
|
||||||
|
|
||||||
|
// Pages if you click bottom navigation
|
||||||
|
final List<Widget> _contentPages = <Widget>[
|
||||||
|
TabHomePage(),
|
||||||
|
TabWishlistPage(),
|
||||||
|
TabShoppingCartPage(),
|
||||||
|
TabAccountPage(),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// set initial pages for navigation to home page
|
||||||
|
_pageController = PageController(initialPage: 0);
|
||||||
|
_pageController.addListener(_handleTabSelection);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleTabSelection() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_pageController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: PageView(
|
||||||
|
controller: _pageController,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
children: _contentPages.map((Widget content) {
|
||||||
|
return content;
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
currentIndex: _currentIndex,
|
||||||
|
onTap: (value) {
|
||||||
|
_currentIndex = value;
|
||||||
|
_pageController.jumpToPage(value);
|
||||||
|
// this unfocus is to prevent show keyboard in the wishlist page when focus on search text field
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
|
selectedFontSize: 8,
|
||||||
|
unselectedFontSize: 8,
|
||||||
|
iconSize: 28,
|
||||||
|
selectedLabelStyle: TextStyle(
|
||||||
|
color: _currentIndex == 1 ? ASSENT_COLOR : PRIMARY_COLOR,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
unselectedLabelStyle:
|
||||||
|
TextStyle(color: CHARCOAL, fontWeight: FontWeight.bold),
|
||||||
|
selectedItemColor: _currentIndex == 1 ? ASSENT_COLOR : PRIMARY_COLOR,
|
||||||
|
unselectedItemColor: CHARCOAL,
|
||||||
|
items: [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
label: 'Home',
|
||||||
|
icon: Icon(Icons.home,
|
||||||
|
color: _currentIndex == 0 ? PRIMARY_COLOR : CHARCOAL)),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
label: 'Wishlist',
|
||||||
|
icon: Icon(Icons.favorite,
|
||||||
|
color: _currentIndex == 1 ? ASSENT_COLOR : CHARCOAL)),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
label: 'Cart',
|
||||||
|
icon: Icon(Icons.shopping_cart,
|
||||||
|
color: _currentIndex == 2 ? PRIMARY_COLOR : CHARCOAL)),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
label: 'Account',
|
||||||
|
icon: Icon(Icons.person_outline,
|
||||||
|
color: _currentIndex == 3 ? PRIMARY_COLOR : CHARCOAL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/coupon_model.dart';
|
||||||
|
import 'package:mobdr/ui/home/coupon_detail.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class CouponPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_CouponPageState createState() => _CouponPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CouponPageState extends State<CouponPage> {
|
||||||
|
TextEditingController _etSearch = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etSearch.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Coupon',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Colors.grey[100]!,
|
||||||
|
width: 1.0,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||||
|
height: kToolbarHeight,
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _etSearch,
|
||||||
|
textAlignVertical: TextAlignVertical.bottom,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
fillColor: Colors.grey[100],
|
||||||
|
filled: true,
|
||||||
|
hintText: 'Enter promo code',
|
||||||
|
prefixIcon: Icon(Icons.search, color: Colors.grey[500]),
|
||||||
|
suffixIcon: (_etSearch.text == '')
|
||||||
|
? null
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_etSearch = TextEditingController(text: '');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(Icons.close, color: Colors.grey[500])),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
borderSide: BorderSide(color: Colors.grey[200]!)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
borderSide: BorderSide(color: Colors.grey[200]!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
preferredSize: Size.fromHeight(kToolbarHeight),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: couponData.length,
|
||||||
|
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildCouponCard(couponData[index]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCouponCard(CouponModel couponData) {
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
CouponDetailPage(couponData: couponData)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE, borderRadius: BorderRadius.circular(5)),
|
||||||
|
child: Text('Limited Offer',
|
||||||
|
style: GlobalStyle.couponLimitedOffer),
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Text(couponData.name, style: GlobalStyle.couponName),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
GlobalStyle.iconTime,
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Text('Expiring in ' + couponData.day + ' days',
|
||||||
|
style: GlobalStyle.couponExpired),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Coupon applied',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('Use Now',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/coupon_model.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class CouponDetailPage extends StatefulWidget {
|
||||||
|
final bool fromList;
|
||||||
|
final CouponModel couponData;
|
||||||
|
|
||||||
|
const CouponDetailPage(
|
||||||
|
{Key? key, required this.couponData, this.fromList = false})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CouponDetailPageState createState() => _CouponDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CouponDetailPageState extends State<CouponDetailPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Coupon Detail',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: EdgeInsets.fromLTRB(24, 20, 24, 24),
|
||||||
|
children: [
|
||||||
|
_buildCouponCard(widget.couponData),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 24),
|
||||||
|
child: Text('Terms and Conditions',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
child: Text(widget.couponData.term),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Coupon applied', toastLength: Toast.LENGTH_LONG);
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (!widget.fromList) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Use',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCouponCard(CouponModel couponData) {
|
||||||
|
return Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(couponData.name,
|
||||||
|
style: GlobalStyle.couponName.copyWith(fontSize: 18)),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text('Limited Offer',
|
||||||
|
style: GlobalStyle.couponLimitedOffer),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
GlobalStyle.iconTime,
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Text('Expiring in ' + couponData.day + ' days',
|
||||||
|
style: GlobalStyle.couponExpired),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,344 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/flashsale_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/home/search.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FlashSalePage extends StatefulWidget {
|
||||||
|
final int seconds;
|
||||||
|
|
||||||
|
const FlashSalePage({Key? key, this.seconds = 0}) : super(key: key);
|
||||||
|
@override
|
||||||
|
_FlashSalePageState createState() => _FlashSalePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FlashSalePageState extends State<FlashSalePage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
Timer? _flashsaleTimer;
|
||||||
|
late int _flashsaleSecond;
|
||||||
|
|
||||||
|
void _startFlashsaleTimer() {
|
||||||
|
const period = const Duration(seconds: 1);
|
||||||
|
_flashsaleTimer = Timer.periodic(period, (timer) {
|
||||||
|
setState(() {
|
||||||
|
_flashsaleSecond--;
|
||||||
|
});
|
||||||
|
if (_flashsaleSecond == 0) {
|
||||||
|
_cancelFlashsaleTimer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelFlashsaleTimer() {
|
||||||
|
if (_flashsaleTimer != null) {
|
||||||
|
_flashsaleTimer?.cancel();
|
||||||
|
_flashsaleTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_flashsaleSecond = widget.seconds;
|
||||||
|
if (_flashsaleSecond != 0) {
|
||||||
|
_startFlashsaleTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_cancelFlashsaleTimer();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Flash Sale',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.search, color: BLACK_GREY),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => SearchPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
buildCacheNetworkImage(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.width / 2,
|
||||||
|
url: GLOBAL_URL + '/flashsale/1.jpg'),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Flash sale end in :',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
color: CHARCOAL)),
|
||||||
|
_buildFlashsaleTime(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CustomScrollView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: false,
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
childAspectRatio:
|
||||||
|
GlobalStyle.gridDelegateFlashsaleRatio,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return _buildFlashsaleCard(index);
|
||||||
|
},
|
||||||
|
childCount: flashsaleData.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlashsaleTime() {
|
||||||
|
int hour = _flashsaleSecond ~/ 3600;
|
||||||
|
int minute = _flashsaleSecond % 3600 ~/ 60;
|
||||||
|
int second = _flashsaleSecond % 60;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text(_globalFunction.formatTime(hour),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Text(' : ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text(_globalFunction.formatTime(minute),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Text(' : ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text(_globalFunction.formatTime(second),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlashsaleCard(index) {
|
||||||
|
final double boxImageSize =
|
||||||
|
((MediaQuery.of(context).size.width) - 24) / 2 - 12;
|
||||||
|
return Container(
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: flashsaleData[index].name,
|
||||||
|
image: flashsaleData[index].image,
|
||||||
|
price: flashsaleData[index].price,
|
||||||
|
rating: 4,
|
||||||
|
review: 45,
|
||||||
|
sale: flashsaleData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: flashsaleData[index].image)),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
top: 10,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(6),
|
||||||
|
bottomLeft: Radius.circular(6))),
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 6, 12, 6),
|
||||||
|
child: Text(
|
||||||
|
flashsaleData[index].discount.toString() + '%',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 13)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
flashsaleData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
(100 - flashsaleData[index].discount) *
|
||||||
|
flashsaleData[index].price /
|
||||||
|
100),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
flashsaleData[index].price),
|
||||||
|
style: GlobalStyle.productPriceDiscounted,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_createAvailableBar(flashsaleData[index].sale.toDouble(),
|
||||||
|
flashsaleData[index].countItem),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
flashsaleData[index].countItem -
|
||||||
|
flashsaleData[index].sale ==
|
||||||
|
0
|
||||||
|
? 'Sold out'
|
||||||
|
: 'Available',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createAvailableBar(double sale, double total) {
|
||||||
|
final double availableWidth =
|
||||||
|
((MediaQuery.of(context).size.width) - 24) / 2 - 28;
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 10),
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: sale / total * (availableWidth),
|
||||||
|
height: 5,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
bottomLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(sale == total ? 10 : 0),
|
||||||
|
bottomRight: Radius.circular(sale == total ? 10 : 0)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: (total - sale) / total * (availableWidth),
|
||||||
|
height: 5,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(sale == 0 ? 10 : 0),
|
||||||
|
bottomLeft: Radius.circular(sale == 0 ? 10 : 0),
|
||||||
|
topRight: Radius.circular(10),
|
||||||
|
bottomRight: Radius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/last_search_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/home/search.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LastSearchPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_LastSearchPageState createState() => _LastSearchPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LastSearchPageState extends State<LastSearchPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Last Search',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.search, color: BLACK_GREY),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => SearchPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: CustomScrollView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: false,
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
childAspectRatio: GlobalStyle.gridDelegateRatio,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return _buildLastSearchCard(index);
|
||||||
|
},
|
||||||
|
childCount: lastSearchData.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLastSearchCard(index) {
|
||||||
|
final double boxImageSize =
|
||||||
|
((MediaQuery.of(context).size.width) - 24) / 2 - 12;
|
||||||
|
return Container(
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: lastSearchData[index].name,
|
||||||
|
image: lastSearchData[index].image,
|
||||||
|
price: lastSearchData[index].price,
|
||||||
|
rating: lastSearchData[index].rating,
|
||||||
|
review: lastSearchData[index].review,
|
||||||
|
sale: lastSearchData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: lastSearchData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
lastSearchData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
lastSearchData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
Text(lastSearchData[index].sale.toString() + ' Sale',
|
||||||
|
style: GlobalStyle.productSale)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.location_on, color: SOFT_GREY, size: 12),
|
||||||
|
Text(' ' + lastSearchData[index].location,
|
||||||
|
style: GlobalStyle.productLocation)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: lastSearchData[index].rating, size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
lastSearchData[index].review.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,453 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/category_all_product_model.dart';
|
||||||
|
import 'package:mobdr/model/category_banner_model.dart';
|
||||||
|
import 'package:mobdr/model/category_new_product_model.dart';
|
||||||
|
import 'package:mobdr/model/category_trending_product_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/home/search.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ProductCategoryPage extends StatefulWidget {
|
||||||
|
final int categoryId;
|
||||||
|
final String categoryName;
|
||||||
|
|
||||||
|
const ProductCategoryPage(
|
||||||
|
{Key? key, this.categoryId = 0, required this.categoryName})
|
||||||
|
: super(key: key);
|
||||||
|
@override
|
||||||
|
_ProductCategoryPageState createState() => _ProductCategoryPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductCategoryPageState extends State<ProductCategoryPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
int _currentImageSlider = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 3);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
widget.categoryName.replaceAll('\n', ' '),
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.search, color: BLACK_GREY),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => SearchPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
_createCategorySlider(),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20, left: 16, right: 16),
|
||||||
|
child:
|
||||||
|
Text('Trending Product', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
height: boxImageSize *
|
||||||
|
GlobalStyle.horizontalProductHeightMultiplication,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: categoryTrendingProductData.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildTrendingProductCard(index, boxImageSize);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20, left: 16, right: 16),
|
||||||
|
child: Text('New Product', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
height: boxImageSize *
|
||||||
|
GlobalStyle.horizontalProductHeightMultiplication,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: categoryNewProductData.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildNewProductCard(index, boxImageSize);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20, left: 16, right: 16),
|
||||||
|
child: Text('All Product', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
CustomScrollView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: false,
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
childAspectRatio: GlobalStyle.gridDelegateRatio,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return _buildAllProductCard(index);
|
||||||
|
},
|
||||||
|
childCount: categoryAllProductData.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createCategorySlider() {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
CarouselSlider(
|
||||||
|
items: categoryBannerData
|
||||||
|
.map((item) => Container(
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0, height: 0, url: item.image),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
options: CarouselOptions(
|
||||||
|
aspectRatio: 2,
|
||||||
|
viewportFraction: 1.0,
|
||||||
|
autoPlay: true,
|
||||||
|
autoPlayInterval: Duration(seconds: 6),
|
||||||
|
autoPlayAnimationDuration: Duration(milliseconds: 300),
|
||||||
|
enlargeCenterPage: false,
|
||||||
|
onPageChanged: (index, reason) {
|
||||||
|
setState(() {
|
||||||
|
_currentImageSlider = index;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: categoryBannerData.map((item) {
|
||||||
|
int index = categoryBannerData.indexOf(item);
|
||||||
|
return Container(
|
||||||
|
width: 8.0,
|
||||||
|
height: 8.0,
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: _currentImageSlider == index
|
||||||
|
? SOFT_BLUE
|
||||||
|
: Colors.grey[300],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTrendingProductCard(index, boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: categoryTrendingProductData[index].name,
|
||||||
|
image: categoryTrendingProductData[index].image,
|
||||||
|
price: categoryTrendingProductData[index].price,
|
||||||
|
rating: categoryTrendingProductData[index].rating,
|
||||||
|
review: categoryTrendingProductData[index].review,
|
||||||
|
sale: 44)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
height: boxImageSize + 10,
|
||||||
|
url: categoryTrendingProductData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
categoryTrendingProductData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
categoryTrendingProductData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: categoryTrendingProductData[index].rating,
|
||||||
|
size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
categoryTrendingProductData[index]
|
||||||
|
.review
|
||||||
|
.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildNewProductCard(index, boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: categoryNewProductData[index].name,
|
||||||
|
image: categoryNewProductData[index].image,
|
||||||
|
price: categoryNewProductData[index].price,
|
||||||
|
rating: categoryNewProductData[index].rating,
|
||||||
|
review: categoryNewProductData[index].review,
|
||||||
|
sale: 36)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
height: boxImageSize + 10,
|
||||||
|
url: categoryNewProductData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
categoryNewProductData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
categoryNewProductData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: categoryNewProductData[index].rating,
|
||||||
|
size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
categoryNewProductData[index]
|
||||||
|
.review
|
||||||
|
.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAllProductCard(index) {
|
||||||
|
final double boxImageSize =
|
||||||
|
((MediaQuery.of(context).size.width) - 24) / 2 - 12;
|
||||||
|
return Container(
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: categoryAllProductData[index].name,
|
||||||
|
image: categoryAllProductData[index].image,
|
||||||
|
price: categoryAllProductData[index].price,
|
||||||
|
rating: categoryAllProductData[index].rating,
|
||||||
|
review: categoryAllProductData[index].review,
|
||||||
|
sale: categoryAllProductData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: categoryAllProductData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
categoryAllProductData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
categoryAllProductData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
Text(
|
||||||
|
categoryAllProductData[index].sale.toString() +
|
||||||
|
' Sale',
|
||||||
|
style: GlobalStyle.productSale)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.location_on, color: SOFT_GREY, size: 12),
|
||||||
|
Text(' ' + categoryAllProductData[index].location,
|
||||||
|
style: GlobalStyle.productLocation)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: categoryAllProductData[index].rating,
|
||||||
|
size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
categoryAllProductData[index]
|
||||||
|
.review
|
||||||
|
.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/last_seen_model.dart';
|
||||||
|
import 'package:mobdr/model/search_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/home/search_product.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SearchPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_SearchPageState createState() => _SearchPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchPageState extends State<SearchPage> {
|
||||||
|
TextEditingController _etSearch = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etSearch.dispose();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 7);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
titleSpacing: 0.0,
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
// create search text field in the app bar
|
||||||
|
title: Container(
|
||||||
|
margin: EdgeInsets.only(right: 16),
|
||||||
|
height: kToolbarHeight - 20,
|
||||||
|
child: TextField(
|
||||||
|
controller: _etSearch,
|
||||||
|
autofocus: true,
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(fontSize: 13, color: Colors.grey[600]),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon:
|
||||||
|
Icon(Icons.search, color: Colors.grey[500], size: 18),
|
||||||
|
suffixIcon: (_etSearch.text == '')
|
||||||
|
? null
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_etSearch = TextEditingController(text: '');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(Icons.close,
|
||||||
|
color: Colors.grey[500], size: 18)),
|
||||||
|
contentPadding: EdgeInsets.all(0),
|
||||||
|
isDense: true,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey[200],
|
||||||
|
hintText: 'Search product',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
// if search field is empty, show history search
|
||||||
|
// if search field not empty, show search text
|
||||||
|
child: _etSearch.text == ''
|
||||||
|
? _showHistorySearch(boxImageSize)
|
||||||
|
: _showSearchText(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _showHistorySearch(boxImageSize) {
|
||||||
|
return ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Text('Last Seen Product',
|
||||||
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
height: boxImageSize,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.only(right: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: lastSeenData.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildLastSeenCard(index, boxImageSize);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 32),
|
||||||
|
child: Text('Last Search',
|
||||||
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: searchData.length,
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 0),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildLastSearchList(index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _showSearchText() {
|
||||||
|
return ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
int idx = searchData
|
||||||
|
.indexWhere((data) => data.words == _etSearch.text);
|
||||||
|
if (idx == -1) {
|
||||||
|
if (searchData.length == 5) {
|
||||||
|
searchData.removeLast();
|
||||||
|
}
|
||||||
|
searchData.insert(
|
||||||
|
0, SearchModel(id: 1, words: _etSearch.text));
|
||||||
|
} else {
|
||||||
|
searchData.removeAt(idx);
|
||||||
|
searchData.insert(
|
||||||
|
0, SearchModel(id: 1, words: _etSearch.text));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
SearchProductPage(words: _etSearch.text)));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.access_time, color: SOFT_GREY, size: 16),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Text(_etSearch.text, style: TextStyle(color: CHARCOAL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLastSeenCard(index, boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(left: index == 0 ? 0 : 12),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: lastSeenData[index].name,
|
||||||
|
image: lastSeenData[index].image,
|
||||||
|
price: lastSeenData[index].price,
|
||||||
|
rating: lastSeenData[index].rating,
|
||||||
|
review: lastSeenData[index].review,
|
||||||
|
sale: lastSeenData[index].sale)));
|
||||||
|
},
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10),
|
||||||
|
),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: lastSeenData[index].image)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLastSearchList(index) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
SearchProductPage(words: searchData[index].words)));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.access_time, color: SOFT_GREY, size: 16),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Flexible(
|
||||||
|
child: Text(searchData[index].words,
|
||||||
|
style: TextStyle(color: CHARCOAL),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
searchData.removeAt(index);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(Icons.close, color: BLACK_GREY, size: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,458 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/search_product_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/home/search.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SearchProductPage extends StatefulWidget {
|
||||||
|
final String words;
|
||||||
|
|
||||||
|
const SearchProductPage({Key? key, this.words = ''}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SearchProductPageState createState() => _SearchProductPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchProductPageState extends State<SearchProductPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
TextEditingController _etSearch = TextEditingController();
|
||||||
|
|
||||||
|
// create sort filter data
|
||||||
|
late List<String> _sortList;
|
||||||
|
int _sortIndex = 0;
|
||||||
|
|
||||||
|
// create other filter 1 data
|
||||||
|
late List<String> _otherFilterOneList;
|
||||||
|
int _otherFilterOneIndex = 0;
|
||||||
|
|
||||||
|
// create other filter 2 data
|
||||||
|
late List<String> _otherFilterTwoList;
|
||||||
|
int _otherFilterTwoIndex = 0;
|
||||||
|
|
||||||
|
// create other filter 3 data
|
||||||
|
late List<String> _otherFilterThreeList;
|
||||||
|
int _otherFilterThreeIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_etSearch.text = widget.words;
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
_initForLang();
|
||||||
|
});
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initForLang() {
|
||||||
|
setState(() {
|
||||||
|
_sortList = [
|
||||||
|
'Relevant Product',
|
||||||
|
'Review',
|
||||||
|
'Newest Product',
|
||||||
|
'Highest Price',
|
||||||
|
'Lowest Price'
|
||||||
|
];
|
||||||
|
_otherFilterOneList = ['Filter 1', 'Filter 2', 'Filter 3', 'Filter 4'];
|
||||||
|
_otherFilterTwoList = [
|
||||||
|
'Filter 1',
|
||||||
|
'Filter 2',
|
||||||
|
'Filter 3',
|
||||||
|
'Filter 4',
|
||||||
|
'Filter 5',
|
||||||
|
'Filter 6',
|
||||||
|
'Filter 7'
|
||||||
|
];
|
||||||
|
_otherFilterThreeList = [
|
||||||
|
'Filter 1',
|
||||||
|
'Filter 2',
|
||||||
|
'Filter 3',
|
||||||
|
'Filter 4',
|
||||||
|
'Filter 5',
|
||||||
|
'Filter 6',
|
||||||
|
'Filter 7',
|
||||||
|
'Filter 8',
|
||||||
|
'Filter 9',
|
||||||
|
'Filter 10'
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etSearch.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
titleSpacing: 0.0,
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: Colors.black, //change your color here
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
elevation: 0,
|
||||||
|
title: Container(
|
||||||
|
margin: EdgeInsets.only(right: 16),
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => Colors.grey[100]!,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => SearchPage()));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Icon(Icons.search, color: Colors.grey[500], size: 18),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
widget.words,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _showFilterPopup();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(right: 16),
|
||||||
|
child: Icon(Icons.filter_list, color: BLACK_GREY))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return Future.value(true);
|
||||||
|
},
|
||||||
|
child: CustomScrollView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: false,
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
childAspectRatio: GlobalStyle.gridDelegateRatio,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return _buildSearchProductCard(index);
|
||||||
|
},
|
||||||
|
childCount: searchProductData.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _showFilterPopup() {
|
||||||
|
// must use StateSetter to update data between main screen and popup.
|
||||||
|
// if use default setState, the data will not update
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter mystate) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 12, bottom: 12),
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(16, 8, 16, 8),
|
||||||
|
child: Text('Filter',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Sort', style: GlobalStyle.filterTitle),
|
||||||
|
Wrap(
|
||||||
|
children: List.generate(_sortList.length, (index) {
|
||||||
|
return _radioSort(_sortList[index], index, mystate);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Text('Other Filter 1', style: GlobalStyle.filterTitle),
|
||||||
|
Wrap(
|
||||||
|
children: List.generate(_otherFilterOneList.length, (index) {
|
||||||
|
return _otherFilterOneSort(
|
||||||
|
_otherFilterOneList[index], index, mystate);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Text('Other Filter 2', style: GlobalStyle.filterTitle),
|
||||||
|
Wrap(
|
||||||
|
children: List.generate(_otherFilterTwoList.length, (index) {
|
||||||
|
return _otherFilterTwoSort(
|
||||||
|
_otherFilterTwoList[index], index, mystate);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Text('Other Filter 3', style: GlobalStyle.filterTitle),
|
||||||
|
Wrap(
|
||||||
|
children:
|
||||||
|
List.generate(_otherFilterThreeList.length, (index) {
|
||||||
|
return _otherFilterThreeSort(
|
||||||
|
_otherFilterThreeList[index], index, mystate);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _radioSort(String txt, int index, mystate) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
mystate(() {
|
||||||
|
_sortIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8, top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _sortIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: _sortIndex == index ? SOFT_BLUE : Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _sortIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _otherFilterOneSort(String txt, int index, mystate) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
mystate(() {
|
||||||
|
_otherFilterOneIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8, top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _otherFilterOneIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: _otherFilterOneIndex == index
|
||||||
|
? SOFT_BLUE
|
||||||
|
: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
_otherFilterOneIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _otherFilterTwoSort(String txt, int index, mystate) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
mystate(() {
|
||||||
|
_otherFilterTwoIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8, top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _otherFilterTwoIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: _otherFilterTwoIndex == index
|
||||||
|
? SOFT_BLUE
|
||||||
|
: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
_otherFilterTwoIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _otherFilterThreeSort(String txt, int index, mystate) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
mystate(() {
|
||||||
|
_otherFilterThreeIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(right: 8, top: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _otherFilterThreeIndex == index ? SOFT_BLUE : Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: _otherFilterThreeIndex == index
|
||||||
|
? SOFT_BLUE
|
||||||
|
: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
|
child: Text(txt,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
_otherFilterThreeIndex == index ? Colors.white : CHARCOAL)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSearchProductCard(index) {
|
||||||
|
final double boxImageSize =
|
||||||
|
((MediaQuery.of(context).size.width) - 24) / 2 - 12;
|
||||||
|
return Container(
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: searchProductData[index].name,
|
||||||
|
image: searchProductData[index].image,
|
||||||
|
price: searchProductData[index].price,
|
||||||
|
rating: searchProductData[index].rating,
|
||||||
|
review: searchProductData[index].review,
|
||||||
|
sale: searchProductData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: searchProductData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
searchProductData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
searchProductData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
Text(
|
||||||
|
searchProductData[index].sale.toString() +
|
||||||
|
' Sale',
|
||||||
|
style: GlobalStyle.productSale)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.location_on, color: SOFT_GREY, size: 12),
|
||||||
|
Text(' ' + searchProductData[index].location,
|
||||||
|
style: GlobalStyle.productLocation)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: searchProductData[index].rating,
|
||||||
|
size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
searchProductData[index].review.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,970 @@
|
||||||
|
/*
|
||||||
|
For this homepage, appBar is created at the bottom after CustomScrollView
|
||||||
|
we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:mobdr/model/category_for_you_model.dart';
|
||||||
|
import 'package:mobdr/model/category_model.dart';
|
||||||
|
import 'package:mobdr/model/flashsale_model.dart';
|
||||||
|
import 'package:mobdr/model/home_banner_model.dart';
|
||||||
|
import 'package:mobdr/model/home_trending_model.dart';
|
||||||
|
import 'package:mobdr/model/last_search_model.dart';
|
||||||
|
import 'package:mobdr/model/recomended_product_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/chat_us.dart';
|
||||||
|
import 'package:mobdr/ui/general/notification.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/home/coupon.dart';
|
||||||
|
import 'package:mobdr/ui/home/flashsale.dart';
|
||||||
|
import 'package:mobdr/ui/home/last_search.dart';
|
||||||
|
import 'package:mobdr/ui/home/product_category.dart';
|
||||||
|
import 'package:mobdr/ui/home/search.dart';
|
||||||
|
import 'package:mobdr/ui/home/search_product.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
|
||||||
|
class TabHomePage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_TabHomePageState createState() => _TabHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TabHomePageState extends State<TabHomePage>
|
||||||
|
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
int _currentImageSlider = 0;
|
||||||
|
|
||||||
|
late ScrollController _scrollController;
|
||||||
|
Color _topIconColor = Colors.white;
|
||||||
|
Color _topSearchColor = Colors.white;
|
||||||
|
late AnimationController _topColorAnimationController;
|
||||||
|
late Animation _appBarColor;
|
||||||
|
SystemUiOverlayStyle _appBarSystemOverlayStyle = SystemUiOverlayStyle.light;
|
||||||
|
|
||||||
|
Timer? _flashsaleTimer;
|
||||||
|
late int _flashsaleSecond;
|
||||||
|
|
||||||
|
void _startFlashsaleTimer() {
|
||||||
|
const period = const Duration(seconds: 1);
|
||||||
|
_flashsaleTimer = Timer.periodic(period, (timer) {
|
||||||
|
setState(() {
|
||||||
|
_flashsaleSecond--;
|
||||||
|
});
|
||||||
|
if (_flashsaleSecond == 0) {
|
||||||
|
_cancelFlashsaleTimer();
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Flash sale is over', toastLength: Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelFlashsaleTimer() {
|
||||||
|
if (_flashsaleTimer != null) {
|
||||||
|
_flashsaleTimer?.cancel();
|
||||||
|
_flashsaleTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep the state to do not refresh when switch navbar
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_setupAnimateAppbar();
|
||||||
|
|
||||||
|
// set how many times left for flashsale
|
||||||
|
var timeNow = DateTime.now();
|
||||||
|
|
||||||
|
// 8000 second = 2 hours 13 minutes 20 second for flashsale timer
|
||||||
|
var flashsaleTime =
|
||||||
|
timeNow.add(Duration(seconds: 8000)).difference(timeNow);
|
||||||
|
_flashsaleSecond = flashsaleTime.inSeconds;
|
||||||
|
_startFlashsaleTimer();
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_topColorAnimationController.dispose();
|
||||||
|
|
||||||
|
_cancelFlashsaleTimer();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setupAnimateAppbar() {
|
||||||
|
// use this function and paramater to animate top bar
|
||||||
|
_topColorAnimationController =
|
||||||
|
AnimationController(vsync: this, duration: Duration(seconds: 0));
|
||||||
|
_appBarColor = ColorTween(begin: Colors.transparent, end: Colors.white)
|
||||||
|
.animate(_topColorAnimationController);
|
||||||
|
_scrollController = ScrollController()
|
||||||
|
..addListener(() {
|
||||||
|
_topColorAnimationController.animateTo(_scrollController.offset / 120);
|
||||||
|
// if scroll for above 150, then change app bar color to white, search button to dark, and top icon color to dark
|
||||||
|
// if scroll for below 150, then change app bar color to transparent, search button to white and top icon color to light
|
||||||
|
if (_scrollController.hasClients &&
|
||||||
|
_scrollController.offset > (150 - kToolbarHeight)) {
|
||||||
|
if (_topIconColor != BLACK_GREY) {
|
||||||
|
_topIconColor = BLACK_GREY;
|
||||||
|
_topSearchColor = Colors.grey[100]!;
|
||||||
|
_appBarSystemOverlayStyle = SystemUiOverlayStyle.dark;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_topIconColor != Colors.white) {
|
||||||
|
_topIconColor = Colors.white;
|
||||||
|
_topSearchColor = Colors.white;
|
||||||
|
_appBarSystemOverlayStyle = SystemUiOverlayStyle.light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context);
|
||||||
|
super.build(context);
|
||||||
|
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 3);
|
||||||
|
final double categoryForYouHeightShort = boxImageSize;
|
||||||
|
final double categoryForYouHeightLong = (boxImageSize * 2);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
CustomScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
slivers: [
|
||||||
|
SliverList(
|
||||||
|
delegate: SliverChildListDelegate([
|
||||||
|
_createHomeBannerSlider(),
|
||||||
|
_createCoupon(),
|
||||||
|
_createGridCategory(),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 10, left: 16, right: 16),
|
||||||
|
child: Text('Flash Sale', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4, left: 16, right: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('Flash sale end in ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
fontSize: 13,
|
||||||
|
color: CHARCOAL)),
|
||||||
|
_buildFlashsaleTime(),
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => FlashSalePage(
|
||||||
|
seconds: _flashsaleSecond)));
|
||||||
|
},
|
||||||
|
child: Text('View All',
|
||||||
|
style: GlobalStyle.viewAll,
|
||||||
|
textAlign: TextAlign.end),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
height: boxImageSize *
|
||||||
|
GlobalStyle.horizontalProductHeightMultiplication,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: flashsaleData.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildFlashsaleCard(index, boxImageSize);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
|
||||||
|
child:
|
||||||
|
Text('Trending Product', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 0, 12, 0),
|
||||||
|
child: GridView.count(
|
||||||
|
padding: EdgeInsets.fromLTRB(0, 8, 0, 0),
|
||||||
|
primary: false,
|
||||||
|
childAspectRatio: 4 / 1.6,
|
||||||
|
shrinkWrap: true,
|
||||||
|
crossAxisSpacing: 2,
|
||||||
|
mainAxisSpacing: 2,
|
||||||
|
crossAxisCount: 2,
|
||||||
|
children: List.generate(homeTrendingData.length, (index) {
|
||||||
|
return _buildTrendingProductCard(index);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Last Search', style: GlobalStyle.sectionTitle),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => LastSearchPage()));
|
||||||
|
},
|
||||||
|
child: Text('View All',
|
||||||
|
style: GlobalStyle.viewAll,
|
||||||
|
textAlign: TextAlign.end),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
height: boxImageSize *
|
||||||
|
GlobalStyle.horizontalProductHeightMultiplication,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: lastSearchData.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildLastSearchCard(index, boxImageSize);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
|
||||||
|
child:
|
||||||
|
Text('Category For You', style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
_createCategoryForYou(boxImageSize, categoryForYouHeightShort,
|
||||||
|
categoryForYouHeightLong),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 30, left: 16, right: 16),
|
||||||
|
child: Text('Recomended Product',
|
||||||
|
style: GlobalStyle.sectionTitle),
|
||||||
|
),
|
||||||
|
CustomScrollView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: false,
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
gridDelegate:
|
||||||
|
SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
childAspectRatio: GlobalStyle.gridDelegateRatio,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return _buildRecomendedProductCard(index);
|
||||||
|
},
|
||||||
|
childCount: recomendedProductData.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// Create AppBar with Animation
|
||||||
|
Container(
|
||||||
|
height: AppBar().preferredSize.height +
|
||||||
|
MediaQuery.of(context).padding.top -
|
||||||
|
20 +
|
||||||
|
22,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _topColorAnimationController,
|
||||||
|
builder: (context, child) => AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
backgroundColor: _appBarColor.value,
|
||||||
|
systemOverlayStyle: _appBarSystemOverlayStyle,
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => _topSearchColor,
|
||||||
|
),
|
||||||
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SearchPage()));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Icon(
|
||||||
|
Icons.search,
|
||||||
|
color: Colors.grey[500],
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Search Product',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ChatUsPage()));
|
||||||
|
},
|
||||||
|
child: Icon(Icons.email, color: _topIconColor)),
|
||||||
|
IconButton(
|
||||||
|
icon: _reusableWidget.customNotifIcon(
|
||||||
|
count: 8, notifColor: _topIconColor),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => NotificationPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createHomeBannerSlider() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
CarouselSlider(
|
||||||
|
items: homeBannerData
|
||||||
|
.map((item) => Container(
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0, height: 0, url: item.image),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
options: CarouselOptions(
|
||||||
|
aspectRatio: 8 / 6,
|
||||||
|
viewportFraction: 1.0,
|
||||||
|
autoPlay: true,
|
||||||
|
autoPlayInterval: Duration(seconds: 6),
|
||||||
|
autoPlayAnimationDuration: Duration(milliseconds: 300),
|
||||||
|
enlargeCenterPage: false,
|
||||||
|
onPageChanged: (index, reason) {
|
||||||
|
setState(() {
|
||||||
|
_currentImageSlider = index;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: homeBannerData.map((item) {
|
||||||
|
int index = homeBannerData.indexOf(item);
|
||||||
|
return Container(
|
||||||
|
width: 8.0,
|
||||||
|
height: 8.0,
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: _currentImageSlider == index
|
||||||
|
? PRIMARY_COLOR
|
||||||
|
: Colors.grey[300],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createCoupon() {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (context) => CouponPage()));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
margin: EdgeInsets.all(16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE, borderRadius: BorderRadius.circular(5)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
child: Text(
|
||||||
|
'There are 10 coupon waiting',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xffffffff),
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(Icons.local_offer, color: Colors.white)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlashsaleTime() {
|
||||||
|
int hour = _flashsaleSecond ~/ 3600;
|
||||||
|
int minute = _flashsaleSecond % 3600 ~/ 60;
|
||||||
|
int second = _flashsaleSecond % 60;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text(_globalFunction.formatTime(hour),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Text(' : ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text(_globalFunction.formatTime(minute),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Text(' : ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(3, 4, 3, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red, borderRadius: BorderRadius.circular(5)), //
|
||||||
|
child: Text(_globalFunction.formatTime(second),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createGridCategory() {
|
||||||
|
return GridView.count(
|
||||||
|
padding: EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||||
|
primary: false,
|
||||||
|
childAspectRatio: 1.1,
|
||||||
|
shrinkWrap: true,
|
||||||
|
crossAxisSpacing: 0,
|
||||||
|
mainAxisSpacing: 0,
|
||||||
|
crossAxisCount: 4,
|
||||||
|
children: List.generate(categoryData.length, (index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: categoryData[index].id,
|
||||||
|
categoryName: categoryData[index].name)));
|
||||||
|
},
|
||||||
|
child: Column(children: [
|
||||||
|
buildCacheNetworkImage(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
url: categoryData[index].image,
|
||||||
|
plColor: Colors.transparent),
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(0, 10, 0, 0),
|
||||||
|
child: Text(
|
||||||
|
categoryData[index].name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlashsaleCard(index, boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: flashsaleData[index].name,
|
||||||
|
image: flashsaleData[index].image,
|
||||||
|
price: flashsaleData[index].price,
|
||||||
|
rating: 4,
|
||||||
|
review: 45,
|
||||||
|
sale: flashsaleData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
height: boxImageSize + 10,
|
||||||
|
url: flashsaleData[index].image)),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
top: 10,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(6),
|
||||||
|
bottomLeft: Radius.circular(6))),
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
child: Text(
|
||||||
|
flashsaleData[index].discount.toString() + '%',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
flashsaleData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
flashsaleData[index].price),
|
||||||
|
style: GlobalStyle.productPriceDiscounted),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 2),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
((100 - flashsaleData[index].discount) *
|
||||||
|
flashsaleData[index].price /
|
||||||
|
100)),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTrendingProductCard(index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
StatefulWidget menuPage =
|
||||||
|
SearchProductPage(words: homeTrendingData[index].name);
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (context) => menuPage));
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
bottomLeft: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width:
|
||||||
|
(MediaQuery.of(context).size.width / 2) * (1.6 / 4) -
|
||||||
|
12 -
|
||||||
|
1,
|
||||||
|
height:
|
||||||
|
(MediaQuery.of(context).size.width / 2) * (1.6 / 4) -
|
||||||
|
12 -
|
||||||
|
1,
|
||||||
|
url: homeTrendingData[index].image)),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(10),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(homeTrendingData[index].name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
Text(homeTrendingData[index].sale + ' Product',
|
||||||
|
style: TextStyle(fontSize: 9, color: BLACK_GREY))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLastSearchCard(index, boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: lastSearchData[index].name,
|
||||||
|
image: lastSearchData[index].image,
|
||||||
|
price: lastSearchData[index].price,
|
||||||
|
rating: lastSearchData[index].rating,
|
||||||
|
review: lastSearchData[index].review,
|
||||||
|
sale: lastSearchData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize + 10,
|
||||||
|
height: boxImageSize + 10,
|
||||||
|
url: lastSearchData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
lastSearchData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
lastSearchData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: lastSearchData[index].rating, size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
lastSearchData[index].review.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createCategoryForYou(
|
||||||
|
boxImageSize, categoryForYouHeightShort, categoryForYouHeightLong) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: categoryForYouHeightLong,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: categoryForYouData[0].id,
|
||||||
|
categoryName: categoryData[0].name)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: categoryForYouHeightLong,
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0, height: 0, url: categoryForYouData[0].image),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: categoryForYouData[1].id,
|
||||||
|
categoryName: categoryData[1].name)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: categoryForYouHeightShort,
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
url: categoryForYouData[1].image),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: categoryForYouData[2].id,
|
||||||
|
categoryName: categoryData[2].name)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: categoryForYouHeightShort,
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
url: categoryForYouData[2].image),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: categoryForYouData[3].id,
|
||||||
|
categoryName: categoryData[3].name)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: categoryForYouHeightShort,
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
url: categoryForYouData[3].image),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductCategoryPage(
|
||||||
|
categoryId: categoryForYouData[4].id,
|
||||||
|
categoryName: categoryData[4].name)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: categoryForYouHeightShort,
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
url: categoryForYouData[4].image),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRecomendedProductCard(index) {
|
||||||
|
final double boxImageSize =
|
||||||
|
((MediaQuery.of(context).size.width) - 24) / 2 - 12;
|
||||||
|
return Container(
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: recomendedProductData[index].name,
|
||||||
|
image: recomendedProductData[index].image,
|
||||||
|
price: recomendedProductData[index].price,
|
||||||
|
rating: recomendedProductData[index].rating,
|
||||||
|
review: recomendedProductData[index].review,
|
||||||
|
sale: recomendedProductData[index].sale)));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: recomendedProductData[index].image)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
recomendedProductData[index].name,
|
||||||
|
style: GlobalStyle.productName,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
recomendedProductData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
Text(
|
||||||
|
recomendedProductData[index].sale.toString() +
|
||||||
|
' Sale',
|
||||||
|
style: TextStyle(fontSize: 11, color: SOFT_GREY))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.location_on, color: SOFT_GREY, size: 12),
|
||||||
|
Text(' ' + recomendedProductData[index].location,
|
||||||
|
style: GlobalStyle.productSale)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: recomendedProductData[index].rating,
|
||||||
|
size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
recomendedProductData[index]
|
||||||
|
.review
|
||||||
|
.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import 'package:mobdr/ui/authentication/signin.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/library/flutter_overboard/overboard.dart';
|
||||||
|
import 'package:mobdr/library/flutter_overboard/page_model.dart';
|
||||||
|
|
||||||
|
class OnBoardingPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_OnBoardingPageState createState() => _OnBoardingPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OnBoardingPageState extends State<OnBoardingPage> {
|
||||||
|
// create each page of onBoard here
|
||||||
|
final _pageList = [
|
||||||
|
PageModel(
|
||||||
|
color: Colors.white,
|
||||||
|
imageAssetPath: LOCAL_IMAGES_URL + '/onboarding/search_product.gif',
|
||||||
|
title: 'Choose Product',
|
||||||
|
body: 'Search and browse the product you want to buy at iJShop',
|
||||||
|
doAnimateImage: true),
|
||||||
|
PageModel(
|
||||||
|
color: Colors.white,
|
||||||
|
imageFromUrl: GLOBAL_URL + '/apps/ecommerce/onboarding/cart.png',
|
||||||
|
title: 'Add to Cart and Pay',
|
||||||
|
body:
|
||||||
|
'Add the product to shopping cart, choose delivery and then pay with your preferences payment',
|
||||||
|
doAnimateImage: true),
|
||||||
|
PageModel(
|
||||||
|
color: Colors.white,
|
||||||
|
imageFromUrl: GLOBAL_URL + '/apps/ecommerce/onboarding/delivery.png',
|
||||||
|
title: 'Delivery',
|
||||||
|
body:
|
||||||
|
'Wait until the product that has been purchased comes to the house',
|
||||||
|
doAnimateImage: true),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function used if user click finish, you won't see this page again until you clear your data of this apps in your phone setting
|
||||||
|
/*void _finishOnBoarding() async {
|
||||||
|
final SharedPreferences _pref = await SharedPreferences.getInstance();
|
||||||
|
await _pref.setBool('onBoarding', false);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: OverBoard(
|
||||||
|
pages: _pageList,
|
||||||
|
showBullets: true,
|
||||||
|
finishCallback: () {
|
||||||
|
//_finishOnBoarding();
|
||||||
|
|
||||||
|
// after you click finish, direct to signin page
|
||||||
|
// Adjust with your need using push replacement or push remove until
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => SigninPage()), (Route<dynamic> route) => false);
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context, MaterialPageRoute(builder: (context) => SigninPage()));
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
CachedNetworkImage buildCacheNetworkImage({double? width, double? height, url, plColor, imageColor}){
|
||||||
|
if(width == 0 && height == 0){
|
||||||
|
return CachedNetworkImage(
|
||||||
|
placeholder: (context, url) {
|
||||||
|
return Container(
|
||||||
|
color: plColor==null?Colors.grey[200]:plColor,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorWidget: (context, url, error) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
imageUrl: url,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
color: imageColor==null?null:imageColor,
|
||||||
|
);
|
||||||
|
} else if(height == 0){
|
||||||
|
return CachedNetworkImage(
|
||||||
|
placeholder: (context, url) {
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
color: plColor==null?Colors.grey[200]:plColor,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorWidget: (context, url, error) {
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
imageUrl: url,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
width: width,
|
||||||
|
color: imageColor==null?null:imageColor,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return CachedNetworkImage(
|
||||||
|
placeholder: (context, url) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
},
|
||||||
|
errorWidget: (context, url, error) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
},
|
||||||
|
imageUrl: url,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
color: imageColor==null?null:imageColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class GlobalFunction{
|
||||||
|
bool validateMobileNumber(String value) {
|
||||||
|
String patttern = r'(^(?:[+0]9)?[0-9]{10,15}$)';
|
||||||
|
RegExp regExp = new RegExp(patttern);
|
||||||
|
if (value.length < 8) {
|
||||||
|
return false;
|
||||||
|
} else if (!regExp.hasMatch(value)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validateEmail(String value) {
|
||||||
|
String pattern = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
|
||||||
|
RegExp regex = new RegExp(pattern);
|
||||||
|
if (!regex.hasMatch(value)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String removeDecimalZeroFormat(double v) {
|
||||||
|
NumberFormat formatter = NumberFormat();
|
||||||
|
formatter.minimumFractionDigits = 0;
|
||||||
|
formatter.maximumFractionDigits = 2;
|
||||||
|
return formatter.format(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatTime(int timeNum) {
|
||||||
|
return timeNum < 10 ? "0" + timeNum.toString() : timeNum.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future showProgressDialog(BuildContext context) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
return Future.value(false);
|
||||||
|
},
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ReusableWidget {
|
||||||
|
PreferredSizeWidget bottomAppBar() {
|
||||||
|
return PreferredSize(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey[100],
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
preferredSize: Size.fromHeight(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget createRatingBar({double rating = 5, double size = 24}) {
|
||||||
|
if (rating < 0) {
|
||||||
|
rating = 0;
|
||||||
|
} else if (rating > 5) {
|
||||||
|
rating = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _absolute = false;
|
||||||
|
int _fullStar = 0;
|
||||||
|
int _emptyStar = 0;
|
||||||
|
|
||||||
|
if (rating == 0 ||
|
||||||
|
rating == 1 ||
|
||||||
|
rating == 2 ||
|
||||||
|
rating == 3 ||
|
||||||
|
rating == 4 ||
|
||||||
|
rating == 5) {
|
||||||
|
_absolute = true;
|
||||||
|
} else {
|
||||||
|
double _dec = (rating - int.parse(rating.toString().substring(0, 1)));
|
||||||
|
if (_dec > 0 && _dec < 1) {
|
||||||
|
if (_dec >= 0.25 && _dec <= 0.75) {
|
||||||
|
_absolute = false;
|
||||||
|
} else {
|
||||||
|
_absolute = true;
|
||||||
|
if (_dec < 0.25) {
|
||||||
|
_emptyStar = 1;
|
||||||
|
} else if (_dec > 0.75) {
|
||||||
|
_fullStar = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
for (int i = 1; i <= rating + _fullStar; i++)
|
||||||
|
Icon(Icons.star, color: Colors.yellow[700], size: size),
|
||||||
|
!_absolute
|
||||||
|
? Icon(Icons.star_half, color: Colors.yellow[700], size: size)
|
||||||
|
: SizedBox.shrink(),
|
||||||
|
for (int i = 1; i <= (5 - rating + _emptyStar); i++)
|
||||||
|
Icon(Icons.star_border, color: Colors.yellow[700], size: size),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget customNotifIcon(
|
||||||
|
{int count = 0,
|
||||||
|
Color notifColor = Colors.grey,
|
||||||
|
Color labelColor = Colors.pinkAccent,
|
||||||
|
double notifSize = 24,
|
||||||
|
double labelSize = 14,
|
||||||
|
String position = 'right'}) {
|
||||||
|
double? posLeft;
|
||||||
|
double? posRight = 0;
|
||||||
|
if (position == 'left') {
|
||||||
|
posLeft = 0;
|
||||||
|
posRight = null;
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.notifications, color: notifColor, size: notifSize),
|
||||||
|
Positioned(
|
||||||
|
left: posLeft,
|
||||||
|
right: posRight,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: labelColor,
|
||||||
|
borderRadius: BorderRadius.circular(labelSize),
|
||||||
|
),
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: labelSize,
|
||||||
|
minHeight: labelSize,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
count.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget divider1() {
|
||||||
|
return Divider(height: 0, color: Colors.grey[400]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _showProgressDialog(BuildContext context) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
return Future.value(false);
|
||||||
|
},
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// dummy loading
|
||||||
|
void startLoading(context, String textMessage, int backToPreviousPageStack) {
|
||||||
|
_showProgressDialog(context);
|
||||||
|
Timer(Duration(seconds: 2), () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
_buildShowDialog(context, textMessage, backToPreviousPageStack);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _buildShowDialog(
|
||||||
|
BuildContext context, String textMessage, int backToPreviousPageStack) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
return Future.value(false);
|
||||||
|
},
|
||||||
|
child: Dialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0)), //this right here
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
margin: EdgeInsets.fromLTRB(40, 20, 40, 20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
textMessage,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 14, color: BLACK_GREY),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
|
||||||
|
if (backToPreviousPageStack > 0) {
|
||||||
|
FocusScope.of(context)
|
||||||
|
.unfocus(); // hide keyboard when press button
|
||||||
|
for (int i = 1;
|
||||||
|
i <= backToPreviousPageStack;
|
||||||
|
i++) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'OK',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// end dummy loading
|
||||||
|
|
||||||
|
Widget createDefaultLabel(context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE, borderRadius: BorderRadius.circular(2)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('Default', style: TextStyle(color: Colors.white, fontSize: 13)),
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Icon(Icons.done, color: Colors.white, size: 11)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/address_model.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ChangeAddressPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_ChangeAddressPageState createState() => _ChangeAddressPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChangeAddressPageState extends State<ChangeAddressPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Change Address',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: addressData.length,
|
||||||
|
// Add one more item for progress indicator
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 0),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return _buildAddressCard(index);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAddressCard(int index) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 6, 12, 0),
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
addressData[index].defaultAddress == true
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(addressData[index].title,
|
||||||
|
style: GlobalStyle.addressTitle),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
borderRadius: BorderRadius.circular(2)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('Default',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white, fontSize: 13)),
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Icon(Icons.done, color: Colors.white, size: 11)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Text(addressData[index].title,
|
||||||
|
style: GlobalStyle.addressTitle),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20),
|
||||||
|
child: Text(addressData[index].recipientName,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(addressData[index].phoneNumber,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(addressData[index].addressLine1,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
addressData[index].addressLine2 +
|
||||||
|
' ' +
|
||||||
|
addressData[index].postalCode,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(addressData[index].state,
|
||||||
|
style: GlobalStyle.addressContent),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
addressData[index].defaultAddress == false
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('Use this',
|
||||||
|
style: GlobalStyle.addressAction),
|
||||||
|
)
|
||||||
|
: Wrap(),
|
||||||
|
index != 0
|
||||||
|
? SizedBox(
|
||||||
|
width: 12,
|
||||||
|
)
|
||||||
|
: Wrap(),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,662 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/shopping_cart_model.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/shopping_cart/change_address.dart';
|
||||||
|
import 'package:mobdr/ui/shopping_cart/payment.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DeliveryPage extends StatefulWidget {
|
||||||
|
final List<ShoppingCartModel> shoppingCartData;
|
||||||
|
|
||||||
|
const DeliveryPage({Key? key, required this.shoppingCartData})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DeliveryPageState createState() => _DeliveryPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DeliveryPageState extends State<DeliveryPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
double _subTotal = 0;
|
||||||
|
String _delivery = '';
|
||||||
|
double _deliveryPrice = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
shoppingCartData = widget.shoppingCartData;
|
||||||
|
countSubTotal();
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void countSubTotal() {
|
||||||
|
_subTotal = 0;
|
||||||
|
for (int i = 0; i < shoppingCartData.length; i++) {
|
||||||
|
_subTotal += shoppingCartData[i].price * shoppingCartData[i].qty;
|
||||||
|
}
|
||||||
|
_subTotal += _deliveryPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 6);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Delivery',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
_createDeliveryInformation(),
|
||||||
|
_createOrderListInformation(boxImageSize),
|
||||||
|
_createChooseDeliveryInformation(),
|
||||||
|
_createSubTotalInformation()
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createDeliveryInformation() {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text('Home Address',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE, borderRadius: BorderRadius.circular(2)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text('Default',
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 13)),
|
||||||
|
SizedBox(
|
||||||
|
width: 4,
|
||||||
|
),
|
||||||
|
Icon(Icons.done, color: Colors.white, size: 11)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text('Robert Steven',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('0811888999',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('6019 Madison St',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('West New York, NJ 07093',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text('USA', style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => ChangeAddressPage()));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
child: Text('Change Address',
|
||||||
|
style: TextStyle(color: PRIMARY_COLOR, fontSize: 14)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createOrderListInformation(boxImageSize) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Order List',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
Column(
|
||||||
|
children: List.generate(shoppingCartData.length, (index) {
|
||||||
|
int quantity = shoppingCartData[index].qty;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
//Navigator.push(context, MaterialPageRoute(builder: (context) => ProductDetailPage(name: shoppingCartData[index].name, image: shoppingCartData[index].image, price: shoppingCartData[index].price, rating: 4, review: 23, sale: 36)));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: shoppingCartData[index].image)),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
shoppingCartData[index].name,
|
||||||
|
style: GlobalStyle.productName.copyWith(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
quantity.toString() + ' item (150 gr)',
|
||||||
|
style: GlobalStyle.shoppingCartOtherProduct
|
||||||
|
.copyWith(color: Colors.grey[400]))),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
'\$' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
quantity *
|
||||||
|
shoppingCartData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createChooseDeliveryInformation() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Delivery',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _showDeliveryPopup();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(width: 1, color: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: _delivery == ''
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.local_shipping, color: SOFT_BLUE),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Text('Choose Delivery',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Icon(Icons.chevron_right, size: 20, color: SOFT_GREY),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(_delivery,
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Text('\$' + _deliveryPrice.toString(),
|
||||||
|
style: GlobalStyle.deliveryTotalPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Icon(Icons.chevron_right, size: 20, color: SOFT_GREY),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _showDeliveryPopup() {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter mystate) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 12, bottom: 12),
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(16, 8, 16, 8),
|
||||||
|
child: Text('Choose Courier', style: GlobalStyle.chooseCourier),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: <Widget>[
|
||||||
|
Text('DHL', style: GlobalStyle.courierTitle),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'DHL Regular';
|
||||||
|
_deliveryPrice = 13;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$13', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'DHL Express';
|
||||||
|
_deliveryPrice = 22;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$22', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('FedEx', style: GlobalStyle.courierTitle),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'FedEx Regular';
|
||||||
|
_deliveryPrice = 9;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'FedEx Express';
|
||||||
|
_deliveryPrice = 17;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 1', style: GlobalStyle.courierTitle),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 1 Regular';
|
||||||
|
_deliveryPrice = 9;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 1 Express';
|
||||||
|
_deliveryPrice = 17;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 2', style: GlobalStyle.courierTitle),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 2 Regular';
|
||||||
|
_deliveryPrice = 9;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 2 Express';
|
||||||
|
_deliveryPrice = 17;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 3', style: GlobalStyle.courierTitle),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 3 Regular';
|
||||||
|
_deliveryPrice = 9;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 3 Express';
|
||||||
|
_deliveryPrice = 17;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Text('Other 4', style: GlobalStyle.courierTitle),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 4 Regular';
|
||||||
|
_deliveryPrice = 9;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Regular', style: GlobalStyle.courierType),
|
||||||
|
Text('\$9', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_delivery = 'Other 4 Express';
|
||||||
|
_deliveryPrice = 17;
|
||||||
|
});
|
||||||
|
countSubTotal();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Express', style: GlobalStyle.courierType),
|
||||||
|
Text('\$17', style: GlobalStyle.deliveryPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createSubTotalInformation() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Sub Total ',
|
||||||
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Text('\$' + _globalFunction.removeDecimalZeroFormat(_subTotal),
|
||||||
|
style: GlobalStyle.shoppingCartTotalPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => PaymentPage()));
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
child: Text(
|
||||||
|
'Pay',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,371 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/ui/home.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class PaymentPage extends StatefulWidget {
|
||||||
|
final bool fromList;
|
||||||
|
|
||||||
|
const PaymentPage({Key? key, this.fromList = false}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PaymentPageState createState() => _PaymentPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaymentPageState extends State<PaymentPage> {
|
||||||
|
// initialize reusable widget
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Payment',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Summary',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: Text('Total Payment',
|
||||||
|
style: TextStyle(color: CHARCOAL, fontSize: 14)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child:
|
||||||
|
Text('\$74', style: GlobalStyle.paymentTotalPrice),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Payment Method',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _createChoosePayment();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text('Change',
|
||||||
|
style:
|
||||||
|
TextStyle(color: PRIMARY_COLOR, fontSize: 14)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
Image.asset('assets/images/visa.png', height: 10),
|
||||||
|
),
|
||||||
|
Text('Visa card ending in 4392',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: CHARCOAL))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(32),
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(3.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showLoading(
|
||||||
|
'Your payment is success, we will prepare your order as soon as possible');
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
'Pay',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createChoosePayment() {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, StateSetter mystate) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(top: 12, bottom: 12),
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(16, 8, 16, 8),
|
||||||
|
child: Text('Payment Method',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: <Widget>[
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
Image.asset('assets/images/visa.png', height: 10),
|
||||||
|
),
|
||||||
|
Text('Visa card ending in 4392')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/mastercard.png',
|
||||||
|
height: 20),
|
||||||
|
),
|
||||||
|
Text('MasterCard ending in 5309')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Color(0xffcccccc),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
Image.asset('assets/images/visa.png', height: 10),
|
||||||
|
),
|
||||||
|
Text('Visa card ending in 2285')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void showLoading(String textMessage) {
|
||||||
|
_progressDialog(context);
|
||||||
|
Timer(Duration(seconds: 2), () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
_buildShowDialog(context, textMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _progressDialog(BuildContext context) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
return Future.value(false);
|
||||||
|
},
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _buildShowDialog(BuildContext context, String textMessage) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
return Future.value(false);
|
||||||
|
},
|
||||||
|
child: Dialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0)), //this right here
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
margin: EdgeInsets.fromLTRB(40, 20, 40, 20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
textMessage,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 14, color: BLACK_GREY),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route<dynamic> route) => false);
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (!widget.fromList) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => HomePage()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 12.0, horizontal: 16),
|
||||||
|
child: Text(
|
||||||
|
'OK',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,369 @@
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/shopping_cart_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/shopping_cart/delivery.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class TabShoppingCartPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_TabShoppingCartPageState createState() => _TabShoppingCartPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TabShoppingCartPageState extends State<TabShoppingCartPage> {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
double _totalPrice = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_countTotalPrice();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _countTotalPrice() {
|
||||||
|
_totalPrice = 0;
|
||||||
|
for (int i = 0; i < shoppingCartData.length; i++) {
|
||||||
|
_totalPrice += shoppingCartData[i].price * shoppingCartData[i].qty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 5);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: GlobalStyle.appBarIconThemeColor,
|
||||||
|
),
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Shopping Cart',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
bottom: _reusableWidget.bottomAppBar(),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
children: List.generate(shoppingCartData.length, (index) {
|
||||||
|
return _buildItem(index, boxImageSize);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_createUseCoupon(),
|
||||||
|
_createTotalPrice(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createUseCoupon() {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
//Navigator.push(context, MaterialPageRoute(builder: (context) => CouponPage()));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
|
margin: EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(width: 1, color: Colors.grey[300]!),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10) // <--- border radius here
|
||||||
|
)),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.local_offer, color: SOFT_BLUE),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Text('Use coupons',
|
||||||
|
style: TextStyle(
|
||||||
|
color: CHARCOAL, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Icon(Icons.chevron_right, size: 20, color: SOFT_GREY),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createTotalPrice() {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Total',
|
||||||
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Text('\$' + _globalFunction.removeDecimalZeroFormat(_totalPrice),
|
||||||
|
style: GlobalStyle.shoppingCartTotalPrice),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) => PRIMARY_COLOR,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
DeliveryPage(shoppingCartData: shoppingCartData)));
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
child: Text(
|
||||||
|
'Next',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Column _buildItem(index, boxImageSize) {
|
||||||
|
int quantity = shoppingCartData[index].qty;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: shoppingCartData[index].name,
|
||||||
|
image: shoppingCartData[index].image,
|
||||||
|
price: shoppingCartData[index].price,
|
||||||
|
rating: 4,
|
||||||
|
review: 23,
|
||||||
|
sale: 36)));
|
||||||
|
},
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: shoppingCartData[index].image)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: shoppingCartData[index].name,
|
||||||
|
image: shoppingCartData[index].image,
|
||||||
|
price: shoppingCartData[index].price,
|
||||||
|
rating: 4,
|
||||||
|
review: 23,
|
||||||
|
sale: 36)));
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
shoppingCartData[index].name,
|
||||||
|
style: GlobalStyle.productName.copyWith(fontSize: 14),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
shoppingCartData[index].price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
showPopupDelete(index, boxImageSize);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
|
||||||
|
height: 30,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
width: 1, color: Colors.grey[300]!)),
|
||||||
|
child: Icon(Icons.delete,
|
||||||
|
color: BLACK_GREY, size: 20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
quantity--;
|
||||||
|
shoppingCartData[index].setQty(quantity);
|
||||||
|
_countTotalPrice();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
|
||||||
|
height: 28,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
borderRadius: BorderRadius.circular(8)),
|
||||||
|
child: Icon(Icons.remove,
|
||||||
|
color: Colors.white, size: 20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Container(
|
||||||
|
child: Text(quantity.toString(),
|
||||||
|
style: TextStyle()),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
quantity++;
|
||||||
|
shoppingCartData[index].setQty(quantity);
|
||||||
|
_countTotalPrice();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
|
||||||
|
height: 28,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
borderRadius: BorderRadius.circular(8)),
|
||||||
|
child: Icon(Icons.add,
|
||||||
|
color: Colors.white, size: 20),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(index == shoppingCartData.length - 1)
|
||||||
|
? Wrap()
|
||||||
|
: Divider(
|
||||||
|
height: 32,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showPopupDelete(index, boxImageSize) {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
shoppingCartData.removeAt(index);
|
||||||
|
});
|
||||||
|
_countTotalPrice();
|
||||||
|
Navigator.pop(context);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Item has been deleted from your Shopping Cart',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Delete from Shopping Cart',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text(
|
||||||
|
'Are you sure to delete this item from your Shopping Cart ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/ui/onboarding.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class SplashScreenPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_SplashScreenPageState createState() => _SplashScreenPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashScreenPageState extends State<SplashScreenPage> {
|
||||||
|
Timer? _timer;
|
||||||
|
int _second = 3; // set timer for 3 second and then direct to next page
|
||||||
|
|
||||||
|
void _startTimer() {
|
||||||
|
const period = const Duration(seconds: 1);
|
||||||
|
_timer = Timer.periodic(period, (timer) {
|
||||||
|
setState(() {
|
||||||
|
_second--;
|
||||||
|
});
|
||||||
|
if (_second == 0) {
|
||||||
|
_cancelFlashsaleTimer();
|
||||||
|
// for this example we will use pushReplacement because we want to go back to the list
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context, MaterialPageRoute(builder: (context) => OnBoardingPage()));
|
||||||
|
|
||||||
|
// if you use this splash screen on the very first time when you open the page, use below code
|
||||||
|
//Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => OnBoardingPage()), (Route<dynamic> route) => false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelFlashsaleTimer() {
|
||||||
|
if (_timer != null) {
|
||||||
|
_timer?.cancel();
|
||||||
|
_timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// set status bar color to transparent and navigation bottom color to black21
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
|
SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_second != 0) {
|
||||||
|
_startTimer();
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_cancelFlashsaleTimer();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: WillPopScope(
|
||||||
|
onWillPop: () {
|
||||||
|
return Future.value(false);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Center(
|
||||||
|
child: Image.asset(LOCAL_IMAGES_URL + '/logo.png',
|
||||||
|
width: MediaQuery.of(context).size.width / 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,378 @@
|
||||||
|
/*
|
||||||
|
This is wishlist page
|
||||||
|
we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:mobdr/config/constant.dart';
|
||||||
|
import 'package:mobdr/config/global_style.dart';
|
||||||
|
import 'package:mobdr/model/wishlist_model.dart';
|
||||||
|
import 'package:mobdr/ui/general/chat_us.dart';
|
||||||
|
import 'package:mobdr/ui/general/notification.dart';
|
||||||
|
import 'package:mobdr/ui/general/product_detail/product_detail.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/cache_image_network.dart';
|
||||||
|
import 'package:mobdr/ui/reusable/global_function.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class TabWishlistPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_TabWishlistPageState createState() => _TabWishlistPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TabWishlistPageState extends State<TabWishlistPage>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
// initialize global function and reusable widget
|
||||||
|
final _globalFunction = GlobalFunction();
|
||||||
|
final _reusableWidget = ReusableWidget();
|
||||||
|
|
||||||
|
// _listKey is used for AnimatedList
|
||||||
|
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
|
||||||
|
|
||||||
|
TextEditingController _etSearch = TextEditingController();
|
||||||
|
|
||||||
|
// keep the state to do not refresh when switch navbar
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_etSearch.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context);
|
||||||
|
super.build(context);
|
||||||
|
final double boxImageSize = (MediaQuery.of(context).size.width / 4);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
elevation: GlobalStyle.appBarElevation,
|
||||||
|
title: Text(
|
||||||
|
'Wishlist',
|
||||||
|
style: GlobalStyle.appBarTitle,
|
||||||
|
),
|
||||||
|
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
||||||
|
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
||||||
|
actions: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => ChatUsPage()));
|
||||||
|
},
|
||||||
|
child: Icon(Icons.email, color: BLACK_GREY)),
|
||||||
|
IconButton(
|
||||||
|
icon: _reusableWidget.customNotifIcon(
|
||||||
|
count: 8, notifColor: BLACK_GREY),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => NotificationPage()));
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
// create search text field in the app bar
|
||||||
|
bottom: PreferredSize(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Colors.grey[100]!,
|
||||||
|
width: 1.0,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||||
|
height: kToolbarHeight,
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _etSearch,
|
||||||
|
textAlignVertical: TextAlignVertical.bottom,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||||
|
onChanged: (textValue) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
fillColor: Colors.grey[100],
|
||||||
|
filled: true,
|
||||||
|
hintText: 'Search Wishlist',
|
||||||
|
prefixIcon: Icon(Icons.search, color: Colors.grey[500]),
|
||||||
|
suffixIcon: (_etSearch.text == '')
|
||||||
|
? null
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_etSearch = TextEditingController(text: '');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(Icons.close, color: Colors.grey[500])),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
borderSide: BorderSide(color: Colors.grey[200]!)),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
borderSide: BorderSide(color: Colors.grey[200]!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
preferredSize: Size.fromHeight(kToolbarHeight),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: AnimatedList(
|
||||||
|
key: _listKey,
|
||||||
|
initialItemCount: wishlistData.length,
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index, animation) {
|
||||||
|
return _buildWishlistCard(
|
||||||
|
wishlistData[index], boxImageSize, animation, index);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildWishlistCard(
|
||||||
|
WishlistModel wishlistData, boxImageSize, animation, index) {
|
||||||
|
return SizeTransition(
|
||||||
|
sizeFactor: animation,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(12, 6, 12, 0),
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(8),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ProductDetailPage(
|
||||||
|
name: wishlistData.name,
|
||||||
|
image: wishlistData.image,
|
||||||
|
price: wishlistData.price,
|
||||||
|
rating: wishlistData.rating,
|
||||||
|
review: wishlistData.review,
|
||||||
|
sale: wishlistData.sale)));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
child: buildCacheNetworkImage(
|
||||||
|
width: boxImageSize,
|
||||||
|
height: boxImageSize,
|
||||||
|
url: wishlistData.image)),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
wishlistData.name,
|
||||||
|
style: GlobalStyle.productName
|
||||||
|
.copyWith(fontSize: 13),
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
'\$ ' +
|
||||||
|
_globalFunction.removeDecimalZeroFormat(
|
||||||
|
wishlistData.price),
|
||||||
|
style: GlobalStyle.productPrice),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.location_on,
|
||||||
|
color: SOFT_GREY, size: 12),
|
||||||
|
Text(' ' + wishlistData.location,
|
||||||
|
style: GlobalStyle.productLocation)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_reusableWidget.createRatingBar(
|
||||||
|
rating: wishlistData.rating, size: 12),
|
||||||
|
Text(
|
||||||
|
'(' +
|
||||||
|
wishlistData.review.toString() +
|
||||||
|
')',
|
||||||
|
style: GlobalStyle.productTotalReview)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
wishlistData.sale.toString() + ' Sale',
|
||||||
|
style: GlobalStyle.productSale),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
showPopupDeleteTabWishlist(index, boxImageSize);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
|
||||||
|
height: 30,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
width: 1, color: Colors.grey[300]!)),
|
||||||
|
child:
|
||||||
|
Icon(Icons.delete, color: BLACK_GREY, size: 20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: (wishlistData.stock == 0)
|
||||||
|
? TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
minimumSize:
|
||||||
|
MaterialStateProperty.all(Size(0, 30)),
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) =>
|
||||||
|
Colors.grey[300]!,
|
||||||
|
),
|
||||||
|
overlayColor: MaterialStateProperty.all(
|
||||||
|
Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
onPressed: null,
|
||||||
|
child: Text(
|
||||||
|
'Out of Stock',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 13),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
))
|
||||||
|
: OutlinedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg:
|
||||||
|
'Item has been added to Shopping Cart');
|
||||||
|
},
|
||||||
|
style: ButtonStyle(
|
||||||
|
minimumSize:
|
||||||
|
MaterialStateProperty.all(Size(0, 30)),
|
||||||
|
overlayColor: MaterialStateProperty.all(
|
||||||
|
Colors.transparent),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
)),
|
||||||
|
side: MaterialStateProperty.all(
|
||||||
|
BorderSide(color: SOFT_BLUE, width: 1.0),
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
'Add to Shopping Cart',
|
||||||
|
style: TextStyle(
|
||||||
|
color: SOFT_BLUE,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 13),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showPopupDeleteTabWishlist(index, boxImageSize) {
|
||||||
|
// set up the buttons
|
||||||
|
Widget cancelButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('No', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
Widget continueButton = TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
int removeIndex = index;
|
||||||
|
var removedItem = wishlistData.removeAt(removeIndex);
|
||||||
|
// This builder is just so that the animation has something
|
||||||
|
// to work with before it disappears from view since the original
|
||||||
|
// has already been deleted.
|
||||||
|
AnimatedRemovedItemBuilder builder = (context, animation) {
|
||||||
|
// A method to build the Card widget.
|
||||||
|
return _buildWishlistCard(
|
||||||
|
removedItem, boxImageSize, animation, removeIndex);
|
||||||
|
};
|
||||||
|
_listKey.currentState!.removeItem(removeIndex, builder);
|
||||||
|
|
||||||
|
Navigator.pop(context);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Item has been deleted from your wishlist',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
},
|
||||||
|
child: Text('Yes', style: TextStyle(color: SOFT_BLUE)));
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'Delete Wishlist',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
content: Text('Are you sure to delete this item from your Wishlist ?',
|
||||||
|
style: TextStyle(fontSize: 13, color: BLACK_GREY)),
|
||||||
|
actions: [
|
||||||
|
cancelButton,
|
||||||
|
continueButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import package_info_plus
|
||||||
|
import path_provider_foundation
|
||||||
|
import sqflite
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||||
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
platform :osx, '10.14'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_macos_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
use_modular_headers!
|
||||||
|
|
||||||
|
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_macos_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
320
pubspec.lock
|
|
@ -17,6 +17,38 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.3"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
carousel_slider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: carousel_slider
|
||||||
|
sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -41,6 +73,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.0"
|
version: "1.17.0"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -57,24 +105,101 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.4"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_lints:
|
flutter_blurhash:
|
||||||
dependency: "direct dev"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_blurhash
|
||||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "0.7.0"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
|
flutter_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_html
|
||||||
|
sha256: "342c7908f0a67bcec62b6e0f7cf23e23bafe7f64693665dd35be98d5e783bdfd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0-alpha.6"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
fluttertoast:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fluttertoast
|
||||||
|
sha256: "774fa28b07f3a82c93596bc137be33189fec578ed3447a93a5a11c93435de394"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.1.3"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.1"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.13.5"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -83,14 +208,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.5"
|
version: "0.6.5"
|
||||||
lints:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: lints
|
|
||||||
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -115,6 +232,38 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
|
numerus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: numerus
|
||||||
|
sha256: "436759d84f233b40107d0cc31cfa92d24e0960afeb2e506be70926d4cddffd9e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
octo_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: octo_image
|
||||||
|
sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
package_info_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: package_info_plus
|
||||||
|
sha256: "8df5ab0a481d7dc20c0e63809e90a588e496d276ba53358afc4c4443d0a00697"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
package_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus_platform_interface
|
||||||
|
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -123,6 +272,94 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.2"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.13"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "7623b7d4be0f0f7d9a8b5ee6879fc13e4522d4c875ab86801dee4af32b54b83e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.23"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: eec003594f19fe2456ea965ae36b3fc967bc5005f508890aafe31fa75e41d972
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.9"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: "642ddf65fde5404f83267e8459ddb4556316d3ee6d511ed193357e25caa3632d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
pedantic:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pedantic
|
||||||
|
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.1"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.4"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.27.7"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -136,6 +373,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4+1"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2+2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -160,6 +413,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -176,6 +437,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.4.16"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.7"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -184,5 +461,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.0 <4.0.0"
|
dart: ">=2.19.0 <4.0.0"
|
||||||
|
flutter: ">=3.3.0"
|
||||||
|
|
|
||||||
22
pubspec.yaml
|
|
@ -34,7 +34,13 @@ dependencies:
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: 1.0.5
|
||||||
|
fluttertoast: 8.1.3
|
||||||
|
package_info_plus: 3.0.3
|
||||||
|
flutter_html: 3.0.0-alpha.6
|
||||||
|
intl: 0.17.0
|
||||||
|
carousel_slider: 4.2.1
|
||||||
|
cached_network_image: 3.2.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
@ -45,7 +51,8 @@ dev_dependencies:
|
||||||
# activated in the `analysis_options.yaml` file located at the root of your
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^2.0.0
|
#FBE nolint
|
||||||
|
#flutter_lints: ^2.0.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
@ -88,3 +95,14 @@ flutter:
|
||||||
#
|
#
|
||||||
# For details regarding fonts from package dependencies,
|
# For details regarding fonts from package dependencies,
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
# see https://flutter.dev/custom-fonts/#from-packages
|
||||||
|
|
||||||
|
assets:
|
||||||
|
- assets/images/google.png
|
||||||
|
- assets/images/facebook.png
|
||||||
|
- assets/images/twitter.png
|
||||||
|
- assets/images/whatsapp.png
|
||||||
|
- assets/images/placeholder.jpg
|
||||||
|
- assets/images/visa.png
|
||||||
|
- assets/images/mastercard.png
|
||||||
|
- assets/images/logo.png
|
||||||
|
- assets/images/onboarding/search_product.gif
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// This is a basic Flutter widget test.
|
// This is a basic Flutter widget test.
|
||||||
//
|
//
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
// utility that Flutter provides. For example, you can send tap and scroll
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
// tree, read text, and verify that the values of widget properties are correct.
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ import 'package:mobdr/main.dart';
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
await tester.pumpWidget(const MyApp());
|
await tester.pumpWidget(MyApp());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Verify that our counter starts at 0.
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
|
|
||||||