après copie des sources appli ecommerce

release/mobdr-v0.0.1
Frédérik Benoist 2023-02-26 11:19:35 +01:00
parent 3af882f13e
commit 7cea57d5ec
96 changed files with 15106 additions and 118 deletions

View File

@ -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

BIN
assets/images/facebook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

BIN
assets/images/google.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
assets/images/twitter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/visa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/images/whatsapp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -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

View File

@ -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 */

28
lib/config/constant.dart Normal file
View File

@ -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';

View File

@ -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);
}

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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,
),
);
}
}

View File

@ -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();
}
}

View File

@ -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});
}

View File

@ -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.
); );
} }
} }

View File

@ -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
),
];

View File

@ -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')
];

View File

@ -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'),
];

View File

@ -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'),
];

View File

@ -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',
),
];

View File

@ -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),
];

View File

@ -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)
];

38
lib/model/chat_model.dart Normal file
View File

@ -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;
}
}

101
lib/model/coupon_model.dart Normal file
View File

@ -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.'
),
];

View File

@ -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),
];

View File

@ -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'),
];

View File

@ -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'),
];

View File

@ -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')
];

View File

@ -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')
];

View File

@ -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,
),
];

View File

@ -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')
];

View File

@ -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),
];

View File

@ -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'
),
];

View File

@ -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'
),
];

View File

@ -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')
];

View File

@ -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,
),
];

View File

@ -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')
];

74
lib/ui/account/about.dart Normal file
View File

@ -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),
),
],
),
));
}
}

View File

@ -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;
},
);
}
}

View File

@ -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,
),
))),
],
));
}
}

View File

@ -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,
),
)),
),
],
));
}
}

View File

@ -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,
),
)),
),
],
));
}
}

View File

@ -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],
)
],
);
}
}

View File

@ -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;
});
},
);
}
}

View File

@ -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)
],
),
],
));
}
}

View File

@ -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),
)
],
))
],
),
),
),
),
);
}
}

View File

@ -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)),
],
),
),
),
],
),
)
],
);
}
}

View File

@ -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(),
);
}
}

View File

@ -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(),
);
}
}

View File

@ -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;
},
);
}
}

View File

@ -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),
),
},
),
),
));
}
}

View File

@ -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,
),
)),
),
],
));
}
}

View File

@ -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,
),
)),
),
],
));
}
}

View File

@ -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;
},
);
}
}

View File

@ -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),
],
),
),
);
}
}

View File

@ -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),
),
},
),
),
));
}
}

View File

@ -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,
)
],
),
),
)
],
));
}
}

View File

@ -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,
)
],
),
),
),
],
));
}
}

View File

@ -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,
)
],
),
),
),
],
),
));
}
}

345
lib/ui/general/chat_us.dart Normal file
View File

@ -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)),
),
),
],
),
);
}
}

View File

@ -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],
),
],
),
),
);
}
}

View File

@ -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),
],
),
],
),
);
}
}

View File

@ -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,
),
],
),
)
],
));
}
}

View File

@ -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)
],
))
],
);
})),
],
));
}
}

View File

@ -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)
],
),
),
);
}
}

94
lib/ui/home.dart Normal file
View File

@ -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)),
],
),
);
}
}

171
lib/ui/home/coupon.dart Normal file
View File

@ -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)),
),
],
),
],
),
),
),
);
}
}

View File

@ -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),
],
),
],
),
],
),
),
);
}
}

344
lib/ui/home/flashsale.dart Normal file
View File

@ -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)),
),
),
],
),
),
);
}
}

View File

@ -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)
],
),
)
],
),
),
],
),
),
),
);
}
}

View File

@ -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)
],
),
)
],
),
),
],
),
),
),
);
}
}

252
lib/ui/home/search.dart Normal file
View File

@ -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)),
],
),
),
);
}
}

View File

@ -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)
],
),
)
],
),
),
],
),
),
),
);
}
}

970
lib/ui/home/tab_home.dart Normal file
View File

@ -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)
],
),
)
],
),
),
],
),
),
),
);
}
}

70
lib/ui/onboarding.dart Normal file
View File

@ -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()));
},
));
}
}

View File

@ -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,
);
}
}

View File

@ -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(),
),
);
});
}
}

View File

@ -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)
],
),
);
}
}

View File

@ -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(),
],
))
],
),
),
),
);
}
}

View File

@ -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,
),
))
],
),
);
}
}

View File

@ -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,
),
)),
)
],
),
),
),
);
});
}
}

View File

@ -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;
},
);
}
}

78
lib/ui/splash_screen.dart Normal file
View File

@ -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),
),
),
));
}
}

View File

@ -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;
},
);
}
}

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -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"))
} }

40
macos/Podfile Normal file
View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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);