From d12265114d733d5339706bfbd07d6088e40d66c0 Mon Sep 17 00:00:00 2001 From: Fakhira Devina Date: Mon, 17 Feb 2020 13:17:56 +0700 Subject: [PATCH 01/11] Update initial data --- lib/network/cookies_interface.dart | 71 --------------- lib/network/data/network_model.dart | 11 --- lib/network/network_interface.dart | 136 ---------------------------- 3 files changed, 218 deletions(-) delete mode 100644 lib/network/cookies_interface.dart delete mode 100644 lib/network/data/network_model.dart delete mode 100644 lib/network/network_interface.dart diff --git a/lib/network/cookies_interface.dart b/lib/network/cookies_interface.dart deleted file mode 100644 index 5d59773..0000000 --- a/lib/network/cookies_interface.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import '../config/strings.dart'; -import 'package:path_provider/path_provider.dart'; - -class CookiesInterface { - - static Future checkCookieFileAvailability() async { - Directory dir; - await getApplicationDocumentsDirectory().then( - (Directory directory) { - dir = directory; - } - ); - File cookieFile = File(dir.path + "/cookies.json"); - bool cookiesExist = cookieFile.existsSync(); - - return cookiesExist; - } - - static Future createCookieFile(Map responseHeaders) async { - try { - String setCookie; - String csrfToken; - String sessionId; - String userKey; - List cookiesList; - Directory dir; - - await getApplicationDocumentsDirectory().then( - (Directory directory) { - dir = directory; - } - ); - File cookieFile = new File(dir.path + "/cookies.json"); - cookieFile.createSync(); - - setCookie = responseHeaders["set-cookie"]; - if (setCookie != null) { - csrfToken = setCookie.split(";")[0].split("=")[1]; - sessionId = setCookie.split(";")[4].split(",")[1].split("=")[1]; - userKey = KEY; - } - - cookiesList = [ - csrfToken, - sessionId, - userKey, - ]; - - cookieFile.writeAsStringSync(json.encode(cookiesList)); - return; - } on Exception catch (e) { - print(e.toString()); - throw e; - } - } - - static Future> getCookieFile() async { - Directory dir; - await getApplicationDocumentsDirectory().then( - (Directory directory) { - dir = directory; - } - ); - File file = File(dir.path + "/cookies.json"); - - dynamic res = file.readAsStringSync(); - return json.decode(res); - } -} \ No newline at end of file diff --git a/lib/network/data/network_model.dart b/lib/network/data/network_model.dart deleted file mode 100644 index 0b49f62..0000000 --- a/lib/network/data/network_model.dart +++ /dev/null @@ -1,11 +0,0 @@ -class NetworkModel { - var response; - int statusCode; - String errorMessage; - - NetworkModel({ - this.response, - this.statusCode, - this.errorMessage, - }); -} diff --git a/lib/network/network_interface.dart b/lib/network/network_interface.dart deleted file mode 100644 index 698fcbc..0000000 --- a/lib/network/network_interface.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'dart:convert'; -import 'package:ppl_disabilitas/flavor/flavor.dart'; -import 'data/network_model.dart'; -import 'package:http/http.dart' as http; -import '../config/strings.dart'; -import 'cookies_interface.dart'; - -class NetworkInterface { - String url = ApiFlavor.getBaseUrl(); - String key = KEY; - - // POST request - Future post({ - String path, - dynamic bodyParams, - bool isLogin, - }) async { - Map headersJson = await _buildRequestHeader(isLogin); - NetworkModel model; - try { - model = await http.post( - "$url$path", - body: json.encode(bodyParams), - headers: headersJson, - ).then((response) async { - Map responseBody = json.decode(response.body); - if (!isLogin) { - if (responseBody.containsKey("key")) { - setKey(responseBody["key"]); - } - await CookiesInterface.createCookieFile(response.headers); - } - return NetworkModel( - statusCode: response.statusCode, - response: responseBody, - ); - }); - if (model.statusCode >= 400) { - throw Exception(); - } - } on Exception catch (e) { - NetworkModel errorModel = NetworkModel( - statusCode: model.statusCode, - errorMessage: e.toString(), - response: model.response); - model = errorModel; - } - - return model; - } - - // GET request - Future get({ - String path, - bool isLogin, - }) async { - Map headersJson = await _buildRequestHeader(isLogin); - NetworkModel model; - try { - model = await http - .get( - "$url$path", - headers: headersJson, - ) - .then((response) { - dynamic responseBody = json.decode(response.body); - return NetworkModel( - statusCode: response.statusCode, - response: responseBody, - ); - }); - if (model.statusCode >= 400) { - throw Exception(); - } - } on Exception catch (e) { - NetworkModel errorModel = NetworkModel( - statusCode: model.statusCode, - errorMessage: e.toString(), - response: model.response); - model = errorModel; - } - - return model; - } - - // PATCH request - Future patch({ - String path, - dynamic bodyParams, - bool isLogin, - }) async { - NetworkModel model; - Map headersJson = await _buildRequestHeader(isLogin); - try { - model = await http - .patch( - "$url$path", - body: json.encode(bodyParams), - headers: headersJson, - ) - .then((response) { - print(response.body.toString()); - return NetworkModel( - statusCode: response.statusCode, - response: jsonDecode(response.body), - ); - }); - if (model.statusCode >= 400) { - throw Exception(); - } - } on Exception catch (e) { - NetworkModel errorModel = NetworkModel( - statusCode: model.statusCode, - errorMessage: e.toString(), - response: model.response); - model = errorModel; - } - - return model; - } - - Future> _buildRequestHeader(bool isLogin) async { - Map headers = Map(); - headers.putIfAbsent("Content-Type", () => "application/json"); - if (isLogin) { - List cookieFile = await CookiesInterface.getCookieFile(); - setKey(cookieFile[2]); - key = cookieFile[2]; - headers.putIfAbsent("Authorization", () => 'Token $key'); - headers.putIfAbsent("X-CSRFToken", () => cookieFile[0]); - headers.putIfAbsent("Cookie", - () => "csrftoken=${cookieFile[0]};sessionid=${cookieFile[1]}"); - return headers; - } - } -} \ No newline at end of file -- GitLab From 284f0aeee17bd1bd864552d8c1e643c9b305ad88 Mon Sep 17 00:00:00 2001 From: Fakhira Devina Date: Fri, 21 Feb 2020 14:49:06 +0700 Subject: [PATCH 02/11] [CHORES] add API KEY --- android/app/src/main/AndroidManifest.xml | 2 ++ android/settings_aar.gradle | 1 + 2 files changed, 3 insertions(+) create mode 100644 android/settings_aar.gradle diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4b8980c..5f22d9a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -26,5 +26,7 @@ + diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' -- GitLab From d3615efdb3c87a80f82baf07748242bacc986148 Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Tue, 25 Feb 2020 11:23:29 +0700 Subject: [PATCH 03/11] [RED] membuat file dasar login, validator, dan menambahkan file test login_test --- lib/page/login/login.dart | 6 ++++++ lib/utils/validator.dart | 9 +++++++++ test/login_test.dart | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 lib/page/login/login.dart create mode 100644 lib/utils/validator.dart create mode 100644 test/login_test.dart diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart new file mode 100644 index 0000000..c26027a --- /dev/null +++ b/lib/page/login/login.dart @@ -0,0 +1,6 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +class Login { + //TODO +} diff --git a/lib/utils/validator.dart b/lib/utils/validator.dart new file mode 100644 index 0000000..926a901 --- /dev/null +++ b/lib/utils/validator.dart @@ -0,0 +1,9 @@ +class FieldValidator { + static String validateEmail(String value) { + // TODO + } + + static String validatePassword(String value) { + // TODO + } +} \ No newline at end of file diff --git a/test/login_test.dart b/test/login_test.dart new file mode 100644 index 0000000..b758a1f --- /dev/null +++ b/test/login_test.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:ppl_disabilitas/utils/validator.dart'; +import 'package:ppl_disabilitas/page/login/login.dart'; + + +void main() { + test('Empty Email Test', () { + var result = FieldValidator.validateEmail(''); + expect(result, 'Email tidak boleh kosong!'); + }); + + test('Invalid Email Test', () { + var result = FieldValidator.validateEmail('dummy'); + expect(result, 'Masukkan email yang valid!'); + }); + + test('Valid Email Test', () { + var result = FieldValidator.validateEmail('dummy@test.com'); + expect(result, null); + }); + + test('Empty Password Test', () { + var result = FieldValidator.validatePassword(''); + expect(result, 'Password tidak boleh kosong!'); + }); + + test('Invalid Password Test', () { + var result = FieldValidator.validatePassword('abc123456'); + expect(result, 'Password anda salah!'); + }); + + test('Valid Password Test', () { + var result = FieldValidator.validatePassword('abcd1234'); + expect(result, null); + }); +} \ No newline at end of file -- GitLab From 6b5a1a07b09fb69f6d0e13a758cfc2110c47245f Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Sun, 1 Mar 2020 16:41:42 +0700 Subject: [PATCH 04/11] [RED] Add widget test for login page, comment verification tests, and add dependency for network image test failed --- assets/logo/google.png | Bin 0 -> 19417 bytes assets/logo/google.svg | 37 +++++++++++++++++++++ lib/main.dart | 10 ++++++ lib/page/login/login.dart | 6 ++-- pubspec.yaml | 6 ++-- test/login_test.dart | 68 ++++++++++++++++++++++++-------------- 6 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 assets/logo/google.png create mode 100644 assets/logo/google.svg diff --git a/assets/logo/google.png b/assets/logo/google.png new file mode 100644 index 0000000000000000000000000000000000000000..2ef869d64c542bfa652312d35e2fea1757456558 GIT binary patch literal 19417 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

yk_HLbV}FhMpm}4mi^ifkOaZbUO7573 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 + + + + + + + + + +]> + + + + + + + + + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index 4ed6580..4fa28f4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'flavor/flavor.dart'; +import 'page/login/login.dart'; void main() { ApiFlavor.flavor = BuildFlavor.production.toString(); @@ -88,10 +89,19 @@ class _MyHomePageState extends State { Text( 'Hello World', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 40) ), + RaisedButton( + onPressed: () => _navigateToLogin(context), + child: Text('Login'), + ) ], ), ), ); } + + void _navigateToLogin(BuildContext context) { + final route = MaterialPageRoute(builder: (_) => Login()); + Navigator.of(context).push(route); + } } diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index c26027a..9e7d26d 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -1,6 +1,8 @@ import 'dart:async'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:ppl_disabilitas/config/styles.dart'; -class Login { +class Login extends StatefulWidget{ //TODO -} +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 1b41f5a..a82dc00 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + image_test_utils: ^1.0.0 # For information on the generic Dart part of this file, see the @@ -45,9 +46,7 @@ flutter: # To add assets to your application, add an assets section, like this: # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - + # - assets/logo/google.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. @@ -108,6 +107,5 @@ flutter: weight: 700 - asset: assets/fonts/Comfortaa-Bold.ttf weight: 800 - # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/login_test.dart b/test/login_test.dart index b758a1f..91be74a 100644 --- a/test/login_test.dart +++ b/test/login_test.dart @@ -1,38 +1,56 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:image_test_utils/image_test_utils.dart'; import 'package:ppl_disabilitas/utils/validator.dart'; import 'package:ppl_disabilitas/page/login/login.dart'; void main() { - test('Empty Email Test', () { - var result = FieldValidator.validateEmail(''); - expect(result, 'Email tidak boleh kosong!'); + testWidgets('Find Username Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Akun"); + await tester.pumpWidget(MaterialApp(home: Login())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); }); - - test('Invalid Email Test', () { - var result = FieldValidator.validateEmail('dummy'); - expect(result, 'Masukkan email yang valid!'); - }); - - test('Valid Email Test', () { - var result = FieldValidator.validateEmail('dummy@test.com'); - expect(result, null); + + testWidgets('Find Password Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Password"); + await tester.pumpWidget(MaterialApp(home: Login())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); }); - test('Empty Password Test', () { - var result = FieldValidator.validatePassword(''); - expect(result, 'Password tidak boleh kosong!'); - }); - test('Invalid Password Test', () { - var result = FieldValidator.validatePassword('abc123456'); - expect(result, 'Password anda salah!'); - }); - - test('Valid Password Test', () { - var result = FieldValidator.validatePassword('abcd1234'); - expect(result, null); - }); +// test('Empty Email Test', () { +// var result = FieldValidator.validateEmail(''); +// expect(result, 'Email tidak boleh kosong!'); +// }); +// +// test('Invalid Email Test', () { +// var result = FieldValidator.validateEmail('dummy'); +// expect(result, 'Masukkan email yang valid!'); +// }); +// +// test('Valid Email Test', () { +// var result = FieldValidator.validateEmail('dummy@test.com'); +// expect(result, null); +// }); +// +// test('Empty Password Test', () { +// var result = FieldValidator.validatePassword(''); +// expect(result, 'Password tidak boleh kosong!'); +// }); +// +// test('Invalid Password Test', () { +// var result = FieldValidator.validatePassword('abc123456'); +// expect(result, 'Password anda salah!'); +// }); +// +// test('Valid Password Test', () { +// var result = FieldValidator.validatePassword('abcd1234'); +// expect(result, null); +// }); } \ No newline at end of file -- GitLab From 18904e414b27540f703f81183fd280208d133271 Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Sun, 1 Mar 2020 17:03:49 +0700 Subject: [PATCH 05/11] [GREEN] passed widget test for login page --- lib/page/login/login.dart | 193 +++++++++++++++++++++++++++++++++++++- 1 file changed, 192 insertions(+), 1 deletion(-) diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index 9e7d26d..2056a38 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -4,5 +4,196 @@ import 'package:flutter/material.dart'; import 'package:ppl_disabilitas/config/styles.dart'; class Login extends StatefulWidget{ - //TODO + + LoginState createState() => LoginState(); +} + +class LoginState extends State { + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('bisaGO'), + centerTitle: true, + backgroundColor: Color(0xff3A903A), + ), + body: SingleChildScrollView( + child: Center( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 25), + Text('Masuk ke Akun', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, + ), + ), + SizedBox(height: 20), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Nomor Telepon atau Email', + style: TextStyle(fontSize: 18), + key: Key('Text Field Akun'), + ), + SizedBox( + height: 10, + ), + TextField( + decoration: InputDecoration( + contentPadding: EdgeInsets.all(8.0), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Password', + style: TextStyle(fontSize: 18), + key: Key('Text Field Password'), + ), + SizedBox( + height: 10, + ), + TextField( + obscureText: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(5), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.fromLTRB(0, 30, 0, 10), + padding: EdgeInsets.symmetric(vertical: 15), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade200, + offset: Offset(2, 4), + blurRadius: 5, + spreadRadius: 2) + ], + color: Color(0xff3A903A) + ), + child: Text( + 'Masuk', + style: TextStyle(fontSize: 20, color: Colors.white), + ), + ), + Container( + child: Row( + children: [ + SizedBox( + width: 20, + ), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Divider( + thickness: 1, + ), + ), + ), + Text('Atau masuk dengan'), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Divider( + thickness: 1, + ), + ), + ), + SizedBox( + width: 20, + ), + ], + ), + ), + Container( + margin: EdgeInsets.fromLTRB(0, 10, 0, 0), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + border: Border.all(), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade200, + offset: Offset(2, 4), + blurRadius: 5, + spreadRadius: 2) + ], + color: Colors.white, + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + topLeft: Radius.circular(10)), + ), + alignment: Alignment.center, + child: Image( + image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png'), + ), + ), + ), + Expanded( + flex: 6, + child: Container( + padding: EdgeInsets.symmetric(vertical: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + topRight: Radius.circular(10)), + ), + alignment: Alignment.center, + child: Text('Akun Google', + style: TextStyle(fontSize: 20) + ), + ), + ), + ], + ), + ), + ], + ), + ), + ) + ) + ); + } + } \ No newline at end of file -- GitLab From 4b9cd8c20887a13201f5f1a3e12126231d083055 Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Sun, 1 Mar 2020 18:44:36 +0700 Subject: [PATCH 06/11] [RED] Add failed widget test for registrasi page --- lib/main.dart | 12 ++++++- lib/page/registrasi/registrasi.dart | 15 +++++++++ test/registrasi_test.dart | 49 +++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 lib/page/registrasi/registrasi.dart create mode 100644 test/registrasi_test.dart diff --git a/lib/main.dart b/lib/main.dart index 4fa28f4..1b5188a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'flavor/flavor.dart'; import 'page/login/login.dart'; +import 'page/registrasi/registrasi.dart'; void main() { ApiFlavor.flavor = BuildFlavor.production.toString(); @@ -92,7 +93,11 @@ class _MyHomePageState extends State { RaisedButton( onPressed: () => _navigateToLogin(context), child: Text('Login'), - ) + ), + RaisedButton( + onPressed: () => _navigateToRegistrasi(context), + child: Text('Registrasi'), + ), ], ), ), @@ -104,4 +109,9 @@ class _MyHomePageState extends State { final route = MaterialPageRoute(builder: (_) => Login()); Navigator.of(context).push(route); } + + void _navigateToRegistrasi(BuildContext context) { + final route = MaterialPageRoute(builder: (_) => Registrasi()); + Navigator.of(context).push(route); + } } diff --git a/lib/page/registrasi/registrasi.dart b/lib/page/registrasi/registrasi.dart new file mode 100644 index 0000000..556ac58 --- /dev/null +++ b/lib/page/registrasi/registrasi.dart @@ -0,0 +1,15 @@ +import 'dart:async'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:ppl_disabilitas/config/styles.dart'; + +class Registrasi extends StatefulWidget { + RegistrasiState createState() => RegistrasiState(); +} + +class RegistrasiState extends State { + + Widget build(BuildContext context) { + + } +} \ No newline at end of file diff --git a/test/registrasi_test.dart b/test/registrasi_test.dart new file mode 100644 index 0000000..d0abba4 --- /dev/null +++ b/test/registrasi_test.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_test_utils/image_test_utils.dart'; + +import 'package:ppl_disabilitas/utils/validator.dart'; +import 'package:ppl_disabilitas/page/registrasi/registrasi.dart'; + + +void main() { + testWidgets('Find Name Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Nama"); + await tester.pumpWidget(MaterialApp(home: Registrasi())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); + }); + + testWidgets('Find Phone Number Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Nomor Telepon"); + await tester.pumpWidget(MaterialApp(home: Registrasi())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); + }); + + testWidgets('Find Email Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Email"); + await tester.pumpWidget(MaterialApp(home: Registrasi())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); + }); + + testWidgets('Find Password Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Password"); + await tester.pumpWidget(MaterialApp(home: Registrasi())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); + }); + + testWidgets('Find Password Confirmation Text Field', (WidgetTester tester) async { + provideMockedNetworkImages(() async { + final textFieldKey = Key("Text Field Konfirmasi Password"); + await tester.pumpWidget(MaterialApp(home: Registrasi())); + expect(find.byKey(textFieldKey), findsOneWidget); + }); + }); +} \ No newline at end of file -- GitLab From 1df50f772ec7c878059e048955e24c14642dcf70 Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Sun, 1 Mar 2020 19:02:29 +0700 Subject: [PATCH 07/11] [GREEN] Passed widget test for registration page, done registration page without functional, minor layouting fix for login page --- lib/page/login/login.dart | 2 +- lib/page/registrasi/registrasi.dart | 266 +++++++++++++++++++++++++++- 2 files changed, 266 insertions(+), 2 deletions(-) diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index 2056a38..8e81b50 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -139,7 +139,7 @@ class LoginState extends State { ), ), Container( - margin: EdgeInsets.fromLTRB(0, 10, 0, 0), + margin: EdgeInsets.fromLTRB(0, 10, 0, 30), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(10)), diff --git a/lib/page/registrasi/registrasi.dart b/lib/page/registrasi/registrasi.dart index 556ac58..e65bc05 100644 --- a/lib/page/registrasi/registrasi.dart +++ b/lib/page/registrasi/registrasi.dart @@ -10,6 +10,270 @@ class Registrasi extends StatefulWidget { class RegistrasiState extends State { Widget build(BuildContext context) { - + return Scaffold( + appBar: AppBar( + title: Text('bisaGO'), + centerTitle: true, + backgroundColor: Color(0xff3A903A), + ), + body: SingleChildScrollView( + child: Center( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 25), + Text('Daftar Akun Baru', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, + ), + ), + SizedBox(height: 20), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Nama Lengkap', + style: TextStyle(fontSize: 18), + key: Key('Text Field Nama'), + ), + SizedBox( + height: 10, + ), + TextField( + decoration: InputDecoration( + contentPadding: EdgeInsets.all(8.0), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Nomor Telepon', + style: TextStyle(fontSize: 18), + key: Key('Text Field Nomor Telepon'), + ), + SizedBox( + height: 10, + ), + TextField( + decoration: InputDecoration( + contentPadding: EdgeInsets.all(8.0), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Email (Opsional)', + style: TextStyle(fontSize: 18), + key: Key('Text Field Email'), + ), + SizedBox( + height: 10, + ), + TextField( + decoration: InputDecoration( + contentPadding: EdgeInsets.all(8.0), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Password', + style: TextStyle(fontSize: 18), + key: Key('Text Field Password'), + ), + SizedBox( + height: 10, + ), + TextField( + obscureText: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(5), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Konfirmasi Password', + style: TextStyle(fontSize: 18), + key: Key('Text Field Konfirmasi Password'), + ), + SizedBox( + height: 10, + ), + TextField( + obscureText: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(5), + border: OutlineInputBorder( + borderSide: BorderSide(color: Color(0xC4C4C4), + width: 0.0, + ), + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.fromLTRB(0, 30, 0, 10), + padding: EdgeInsets.symmetric(vertical: 15), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade200, + offset: Offset(2, 4), + blurRadius: 5, + spreadRadius: 2) + ], + color: Color(0xff3A903A) + ), + child: Text( + 'Daftar', + style: TextStyle(fontSize: 20, color: Colors.white), + ), + ), + Container( + child: Row( + children: [ + SizedBox( + width: 20, + ), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Divider( + thickness: 1, + ), + ), + ), + Text('Atau masuk dengan'), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Divider( + thickness: 1, + ), + ), + ), + SizedBox( + width: 20, + ), + ], + ), + ), + Container( + margin: EdgeInsets.fromLTRB(0, 10, 0, 30), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + border: Border.all(), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade200, + offset: Offset(2, 4), + blurRadius: 5, + spreadRadius: 2) + ], + color: Colors.white, + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + topLeft: Radius.circular(10)), + ), + alignment: Alignment.center, + child: Image( + image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png'), + ), + ), + ), + Expanded( + flex: 6, + child: Container( + padding: EdgeInsets.symmetric(vertical: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + topRight: Radius.circular(10)), + ), + alignment: Alignment.center, + child: Text('Akun Google', + style: TextStyle(fontSize: 20) + ), + ), + ), + ], + ), + ), + ], + ), + ), + ) + ) + ); } } \ No newline at end of file -- GitLab From 5e6db77befbfc121f6d207e25583f6eabaf241ea Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Tue, 3 Mar 2020 01:43:55 +0700 Subject: [PATCH 08/11] [REFACTOR] Make custom text field and custom button for reusability --- lib/page/login/login.dart | 229 +++++++------------------ lib/page/registrasi/registrasi.dart | 256 ++++------------------------ lib/utils/customButton.dart | 109 ++++++++++++ lib/utils/customTextField.dart | 66 +++++++ 4 files changed, 264 insertions(+), 396 deletions(-) create mode 100644 lib/utils/customButton.dart create mode 100644 lib/utils/customTextField.dart diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index 8e81b50..7e5b25f 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:ppl_disabilitas/config/styles.dart'; +import 'package:ppl_disabilitas/utils/customButton.dart'; +import 'package:ppl_disabilitas/utils/customTextField.dart'; class Login extends StatefulWidget{ @@ -9,6 +11,9 @@ class Login extends StatefulWidget{ } class LoginState extends State { + final GlobalKey _formKey = GlobalKey(); + String _accountCredential; + String _password; Widget build(BuildContext context) { return Scaffold( @@ -18,179 +23,61 @@ class LoginState extends State { backgroundColor: Color(0xff3A903A), ), body: SingleChildScrollView( - child: Center( - child: Container( - margin: EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: 25), - Text('Masuk ke Akun', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 25, - ), - ), - SizedBox(height: 20), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Nomor Telepon atau Email', - style: TextStyle(fontSize: 18), - key: Key('Text Field Akun'), - ), - SizedBox( - height: 10, - ), - TextField( - decoration: InputDecoration( - contentPadding: EdgeInsets.all(8.0), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), - ), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Password', - style: TextStyle(fontSize: 18), - key: Key('Text Field Password'), - ), - SizedBox( - height: 10, - ), - TextField( - obscureText: true, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(5), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), - ), - Container( - margin: EdgeInsets.fromLTRB(0, 30, 0, 10), - padding: EdgeInsets.symmetric(vertical: 15), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - boxShadow: [ - BoxShadow( - color: Colors.grey.shade200, - offset: Offset(2, 4), - blurRadius: 5, - spreadRadius: 2) - ], - color: Color(0xff3A903A) - ), - child: Text( - 'Masuk', - style: TextStyle(fontSize: 20, color: Colors.white), - ), - ), - Container( - child: Row( - children: [ - SizedBox( - width: 20, - ), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider( - thickness: 1, - ), - ), - ), - Text('Atau masuk dengan'), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider( - thickness: 1, - ), - ), - ), - SizedBox( - width: 20, - ), - ], - ), - ), - Container( - margin: EdgeInsets.fromLTRB(0, 10, 0, 30), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - border: Border.all(), - boxShadow: [ - BoxShadow( - color: Colors.grey.shade200, - offset: Offset(2, 4), - blurRadius: 5, - spreadRadius: 2) - ], - color: Colors.white, - ), - child: Row( - children: [ - Expanded( - flex: 1, - child: Container( - padding: EdgeInsets.all(15), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(10), - topLeft: Radius.circular(10)), - ), - alignment: Alignment.center, - child: Image( - image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png'), - ), - ), - ), - Expanded( - flex: 6, - child: Container( - padding: EdgeInsets.symmetric(vertical: 15), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(10), - topRight: Radius.circular(10)), - ), - alignment: Alignment.center, - child: Text('Akun Google', - style: TextStyle(fontSize: 20) - ), - ), + child: Form( + key: _formKey, + child: Center( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 25), + Text('Masuk ke Akun', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, ), - ], - ), + ), + SizedBox(height: 20), + CustomTextField( + title: 'Nomor Telepon atau Email', + key: Key('Text Field Akun'), + onSaved: (input) { + _accountCredential = input; + }, + ), + CustomTextField( + title: 'Password', + key: Key('Text Field Password'), + obsecure: true, + onSaved: (input) { + _password = input; + }, + ), + Container( + margin: EdgeInsets.fromLTRB(0, 30, 0, 10), + alignment: Alignment.center, + child: ButtonTheme( + minWidth: double.infinity, + height: 40, + child: submitButton('Masuk', Color(0xff3A903A), Colors.white, + Color(0xff3A903A), Colors.white), + ) + ), + Container( + child: separator('Atau masuk dengan'), + ), + Container( + margin: EdgeInsets.fromLTRB(0, 10, 0, 30), + alignment: Alignment.center, + child: googleButton(), + ), + ], ), - ], - ), - ), + ), + ) ) ) ); diff --git a/lib/page/registrasi/registrasi.dart b/lib/page/registrasi/registrasi.dart index e65bc05..3cf2e31 100644 --- a/lib/page/registrasi/registrasi.dart +++ b/lib/page/registrasi/registrasi.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:ppl_disabilitas/config/styles.dart'; +import 'package:ppl_disabilitas/utils/customButton.dart'; +import 'package:ppl_disabilitas/utils/customTextField.dart'; class Registrasi extends StatefulWidget { RegistrasiState createState() => RegistrasiState(); @@ -17,6 +19,7 @@ class RegistrasiState extends State { backgroundColor: Color(0xff3A903A), ), body: SingleChildScrollView( + child: Form( child: Center( child: Container( margin: EdgeInsets.symmetric(horizontal: 20.0), @@ -33,247 +36,50 @@ class RegistrasiState extends State { ), ), SizedBox(height: 20), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Nama Lengkap', - style: TextStyle(fontSize: 18), - key: Key('Text Field Nama'), - ), - SizedBox( - height: 10, - ), - TextField( - decoration: InputDecoration( - contentPadding: EdgeInsets.all(8.0), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), + CustomTextField( + title: 'Nama Lengkap', + key: Key('Text Field Nama'), ), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Nomor Telepon', - style: TextStyle(fontSize: 18), - key: Key('Text Field Nomor Telepon'), - ), - SizedBox( - height: 10, - ), - TextField( - decoration: InputDecoration( - contentPadding: EdgeInsets.all(8.0), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), + CustomTextField( + title: 'Nomor Telepon', + key: Key('Text Field Nomor Telepon'), ), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Email (Opsional)', - style: TextStyle(fontSize: 18), - key: Key('Text Field Email'), - ), - SizedBox( - height: 10, - ), - TextField( - decoration: InputDecoration( - contentPadding: EdgeInsets.all(8.0), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), + CustomTextField( + title: 'Email (Opsional)', + key: Key('Text Field Email'), ), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Password', - style: TextStyle(fontSize: 18), - key: Key('Text Field Password'), - ), - SizedBox( - height: 10, - ), - TextField( - obscureText: true, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(5), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), + CustomTextField( + title: 'Password', + key: Key('Text Field Password'), ), - Container( - margin: EdgeInsets.symmetric(vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Konfirmasi Password', - style: TextStyle(fontSize: 18), - key: Key('Text Field Konfirmasi Password'), - ), - SizedBox( - height: 10, - ), - TextField( - obscureText: true, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(5), - border: OutlineInputBorder( - borderSide: BorderSide(color: Color(0xC4C4C4), - width: 0.0, - ), - borderRadius: BorderRadius.circular(10.0), - ), - ), - ), - ], - ), + CustomTextField( + title: 'Konfirmasi Password', + key: Key('Text Field Konfirmasi Password'), ), Container( - margin: EdgeInsets.fromLTRB(0, 30, 0, 10), - padding: EdgeInsets.symmetric(vertical: 15), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - boxShadow: [ - BoxShadow( - color: Colors.grey.shade200, - offset: Offset(2, 4), - blurRadius: 5, - spreadRadius: 2) - ], - color: Color(0xff3A903A) - ), - child: Text( - 'Daftar', - style: TextStyle(fontSize: 20, color: Colors.white), - ), + margin: EdgeInsets.fromLTRB(0, 30, 0, 10), + alignment: Alignment.center, + child: ButtonTheme( + minWidth: double.infinity, + height: 40, + child: submitButton('Daftar', Color(0xff3A903A), Colors.white, + Color(0xff3A903A), Colors.white), + ) ), Container( - child: Row( - children: [ - SizedBox( - width: 20, - ), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider( - thickness: 1, - ), - ), - ), - Text('Atau masuk dengan'), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider( - thickness: 1, - ), - ), - ), - SizedBox( - width: 20, - ), - ], - ), + child: separator('Atau daftar dengan'), ), Container( margin: EdgeInsets.fromLTRB(0, 10, 0, 30), alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - border: Border.all(), - boxShadow: [ - BoxShadow( - color: Colors.grey.shade200, - offset: Offset(2, 4), - blurRadius: 5, - spreadRadius: 2) - ], - color: Colors.white, - ), - child: Row( - children: [ - Expanded( - flex: 1, - child: Container( - padding: EdgeInsets.all(15), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(10), - topLeft: Radius.circular(10)), - ), - alignment: Alignment.center, - child: Image( - image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png'), - ), - ), - ), - Expanded( - flex: 6, - child: Container( - padding: EdgeInsets.symmetric(vertical: 15), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(10), - topRight: Radius.circular(10)), - ), - alignment: Alignment.center, - child: Text('Akun Google', - style: TextStyle(fontSize: 20) - ), - ), - ), - ], - ), + child: googleButton(), ), ], ), ), - ) - ) + ), + ), + ), ); } } \ No newline at end of file diff --git a/lib/utils/customButton.dart b/lib/utils/customButton.dart new file mode 100644 index 0000000..842e084 --- /dev/null +++ b/lib/utils/customButton.dart @@ -0,0 +1,109 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +Widget submitButton(String text, Color splashColor, Color highlightColor, +Color fillColor, Color textColor) { + return RaisedButton( + padding: EdgeInsets.symmetric(vertical: 15), + highlightElevation: 0.0, + splashColor: splashColor, + highlightColor: highlightColor, + elevation: 0.0, + color: fillColor, + shape: RoundedRectangleBorder( + borderRadius: new BorderRadius.all(Radius.circular(10)), + ), + child: Text( + text, + style: TextStyle(fontSize: 20, color: textColor, fontWeight: FontWeight.bold), + ), + onPressed: () { + + }, + ); +} + +Widget separator(String text) { + return Row( + children: [ + SizedBox( + width: 20, + ), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Divider( + thickness: 1, + ), + ), + ), + Text(text), + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Divider( + thickness: 1, + ), + ), + ), + SizedBox( + width: 20, + ), + ], + ); +} + +Widget googleButton() { + return InkWell( + onTap: () { + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + border: Border.all(), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade200, + offset: Offset(2, 4), + blurRadius: 5, + spreadRadius: 2) + ], + color: Colors.white, + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + topLeft: Radius.circular(10)), + ), + alignment: Alignment.center, + child: Image( + image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png'), + ), + ), + ), + Expanded( + flex: 6, + child: Container( + padding: EdgeInsets.symmetric(vertical: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + topRight: Radius.circular(10)), + ), + alignment: Alignment.center, + child: Text('Akun Google', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold) + ), + ), + ), + ], + ), + ), + ); +} \ No newline at end of file diff --git a/lib/utils/customTextField.dart b/lib/utils/customTextField.dart new file mode 100644 index 0000000..971a1ed --- /dev/null +++ b/lib/utils/customTextField.dart @@ -0,0 +1,66 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class CustomTextField extends StatelessWidget { + CustomTextField( + { this.title, + this.hint, + this.obsecure = false, + this.key, + this.validator, + this.onSaved}); + final String title; + final Key key; + final FormFieldSetter onSaved; + final String hint; + final bool obsecure; + final FormFieldValidator validator; + + @override + Widget build(BuildContext context) { + key: key; + return Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(fontSize: 18), + ), + SizedBox( + height: 10, + ), + TextFormField( + onSaved: onSaved, + validator: validator, + autofocus: true, + obscureText: obsecure, + style: TextStyle( + fontSize: 15, + ), + decoration: InputDecoration( + hintStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + hintText: hint, + contentPadding: EdgeInsets.all(8.0), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).primaryColor, + width: 1, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).primaryColor, + width: 1, + ), + ), + ), + ), + ], + ), + ); + } +} \ No newline at end of file -- GitLab From bc4f21c0324c254f9b2f2653fcee8607c54b85ea Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Tue, 3 Mar 2020 02:17:07 +0700 Subject: [PATCH 09/11] [RED] Uncomment validation tests --- lib/page/login/login.dart | 2 +- lib/page/registrasi/registrasi.dart | 1 + test/login_test.dart | 58 ++++++++++++++--------------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index 7e5b25f..404d44c 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -11,7 +11,7 @@ class Login extends StatefulWidget{ } class LoginState extends State { - final GlobalKey _formKey = GlobalKey(); + final GlobalKey _formKey = GlobalKey(); String _accountCredential; String _password; diff --git a/lib/page/registrasi/registrasi.dart b/lib/page/registrasi/registrasi.dart index 3cf2e31..1bb1f39 100644 --- a/lib/page/registrasi/registrasi.dart +++ b/lib/page/registrasi/registrasi.dart @@ -10,6 +10,7 @@ class Registrasi extends StatefulWidget { } class RegistrasiState extends State { + final GlobalKey _formKey = GlobalKey(); Widget build(BuildContext context) { return Scaffold( diff --git a/test/login_test.dart b/test/login_test.dart index 91be74a..90b1c68 100644 --- a/test/login_test.dart +++ b/test/login_test.dart @@ -24,33 +24,33 @@ void main() { }); -// test('Empty Email Test', () { -// var result = FieldValidator.validateEmail(''); -// expect(result, 'Email tidak boleh kosong!'); -// }); -// -// test('Invalid Email Test', () { -// var result = FieldValidator.validateEmail('dummy'); -// expect(result, 'Masukkan email yang valid!'); -// }); -// -// test('Valid Email Test', () { -// var result = FieldValidator.validateEmail('dummy@test.com'); -// expect(result, null); -// }); -// -// test('Empty Password Test', () { -// var result = FieldValidator.validatePassword(''); -// expect(result, 'Password tidak boleh kosong!'); -// }); -// -// test('Invalid Password Test', () { -// var result = FieldValidator.validatePassword('abc123456'); -// expect(result, 'Password anda salah!'); -// }); -// -// test('Valid Password Test', () { -// var result = FieldValidator.validatePassword('abcd1234'); -// expect(result, null); -// }); + test('Empty Email Test', () { + var result = FieldValidator.validateEmail(''); + expect(result, '*Wajib diisi'); + }); + + test('Invalid Email Test', () { + var result = FieldValidator.validateEmail('dummy'); + expect(result, '*Masukkan email yang valid'); + }); + + test('Valid Email Test', () { + var result = FieldValidator.validateEmail('dummy@test.com'); + expect(result, null); + }); + + test('Empty Password Test', () { + var result = FieldValidator.validatePassword(''); + expect(result, '*Wajib diisi'); + }); + + test('Invalid Password Test', () { + var result = FieldValidator.validatePassword('ab456'); + expect(result, '*Password harus lebih dari 6 karakter'); + }); + + test('Valid Password Test', () { + var result = FieldValidator.validatePassword('abcd1234'); + expect(result, null); + }); } \ No newline at end of file -- GitLab From b99c741def2fca951f7ef92c40c58b497c40750f Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Tue, 3 Mar 2020 02:55:32 +0700 Subject: [PATCH 10/11] [GREEN] Passed validation test --- lib/page/login/login.dart | 130 +++++++++++++++------------- lib/page/registrasi/registrasi.dart | 8 +- lib/utils/customButton.dart | 4 +- lib/utils/validator.dart | 15 +++- 4 files changed, 92 insertions(+), 65 deletions(-) diff --git a/lib/page/login/login.dart b/lib/page/login/login.dart index 404d44c..b1183db 100644 --- a/lib/page/login/login.dart +++ b/lib/page/login/login.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:ppl_disabilitas/config/styles.dart'; import 'package:ppl_disabilitas/utils/customButton.dart'; import 'package:ppl_disabilitas/utils/customTextField.dart'; +import 'package:ppl_disabilitas/utils/validator.dart'; class Login extends StatefulWidget{ @@ -17,70 +18,79 @@ class LoginState extends State { Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text('bisaGO'), - centerTitle: true, - backgroundColor: Color(0xff3A903A), - ), - body: SingleChildScrollView( - child: Form( - key: _formKey, - child: Center( - child: Container( - margin: EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: 25), - Text('Masuk ke Akun', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 25, - ), + appBar: AppBar( + title: Text('bisaGO'), + centerTitle: true, + backgroundColor: Color(0xff3A903A), + ), + body: SingleChildScrollView( + child: Form( + key: _formKey, + child: Center( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 25), + Text('Masuk ke Akun', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, + ), + ), + SizedBox(height: 20), + CustomTextField( + title: 'Nomor Telepon atau Email', + key: Key('Text Field Akun'), + onSaved: (input) { + _accountCredential = input; + }, + validator: FieldValidator.validateEmail, + ), + CustomTextField( + title: 'Password', + key: Key('Text Field Password'), + obsecure: true, + onSaved: (input) { + _password = input; + }, + validator: FieldValidator.validatePassword, + ), + Container( + margin: EdgeInsets.fromLTRB(0, 30, 0, 10), + alignment: Alignment.center, + child: ButtonTheme( + minWidth: double.infinity, + height: 40, + child: submitButton( + 'Masuk', Color(0xff3A903A), Colors.white, + Color(0xff3A903A), Colors.white, _validateLoginInput), + ) + ), + Container( + child: separator('Atau masuk dengan'), + ), + Container( + margin: EdgeInsets.fromLTRB(0, 10, 0, 30), + alignment: Alignment.center, + child: googleButton(), + ), + ], ), - SizedBox(height: 20), - CustomTextField( - title: 'Nomor Telepon atau Email', - key: Key('Text Field Akun'), - onSaved: (input) { - _accountCredential = input; - }, - ), - CustomTextField( - title: 'Password', - key: Key('Text Field Password'), - obsecure: true, - onSaved: (input) { - _password = input; - }, - ), - Container( - margin: EdgeInsets.fromLTRB(0, 30, 0, 10), - alignment: Alignment.center, - child: ButtonTheme( - minWidth: double.infinity, - height: 40, - child: submitButton('Masuk', Color(0xff3A903A), Colors.white, - Color(0xff3A903A), Colors.white), - ) - ), - Container( - child: separator('Atau masuk dengan'), - ), - Container( - margin: EdgeInsets.fromLTRB(0, 10, 0, 30), - alignment: Alignment.center, - child: googleButton(), - ), - ], - ), - ), + ), + ) ) ) - ) ); } + void _validateLoginInput() async { + final FormState form = _formKey.currentState; + if(_formKey.currentState.validate()) { + form.save(); + } + } } \ No newline at end of file diff --git a/lib/page/registrasi/registrasi.dart b/lib/page/registrasi/registrasi.dart index 1bb1f39..02b610a 100644 --- a/lib/page/registrasi/registrasi.dart +++ b/lib/page/registrasi/registrasi.dart @@ -64,7 +64,7 @@ class RegistrasiState extends State { minWidth: double.infinity, height: 40, child: submitButton('Daftar', Color(0xff3A903A), Colors.white, - Color(0xff3A903A), Colors.white), + Color(0xff3A903A), Colors.white, _validateLoginInput), ) ), Container( @@ -83,4 +83,10 @@ class RegistrasiState extends State { ), ); } + void _validateLoginInput() async { + final FormState form = _formKey.currentState; + if (_formKey.currentState.validate()) { + form.save(); + } + } } \ No newline at end of file diff --git a/lib/utils/customButton.dart b/lib/utils/customButton.dart index 842e084..b5d46ee 100644 --- a/lib/utils/customButton.dart +++ b/lib/utils/customButton.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; Widget submitButton(String text, Color splashColor, Color highlightColor, -Color fillColor, Color textColor) { +Color fillColor, Color textColor, void function()) { return RaisedButton( padding: EdgeInsets.symmetric(vertical: 15), highlightElevation: 0.0, @@ -18,7 +18,7 @@ Color fillColor, Color textColor) { style: TextStyle(fontSize: 20, color: textColor, fontWeight: FontWeight.bold), ), onPressed: () { - + function(); }, ); } diff --git a/lib/utils/validator.dart b/lib/utils/validator.dart index 926a901..9764987 100644 --- a/lib/utils/validator.dart +++ b/lib/utils/validator.dart @@ -1,9 +1,20 @@ class FieldValidator { static String validateEmail(String value) { - // TODO + Pattern 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 (value.isEmpty) return '*Wajib diisi'; + if (!regex.hasMatch(value)) + return '*Masukkan email yang valid'; + else + return null; } static String validatePassword(String value) { - // TODO + if (value.isEmpty) return '*Wajib diisi'; + + if (value.length < 7) return '*Password harus lebih dari 6 karakter'; + else + return null; } } \ No newline at end of file -- GitLab From 7be2e4fc6ea0659a18c22a70bde6ee5597be36c5 Mon Sep 17 00:00:00 2001 From: Usman Sidiq Date: Tue, 3 Mar 2020 03:14:24 +0700 Subject: [PATCH 11/11] [REFACTOR] Implement validator for registration --- lib/page/registrasi/registrasi.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/page/registrasi/registrasi.dart b/lib/page/registrasi/registrasi.dart index 02b610a..c4e4134 100644 --- a/lib/page/registrasi/registrasi.dart +++ b/lib/page/registrasi/registrasi.dart @@ -21,6 +21,7 @@ class RegistrasiState extends State { ), body: SingleChildScrollView( child: Form( + key: _formKey, child: Center( child: Container( margin: EdgeInsets.symmetric(horizontal: 20.0), @@ -40,10 +41,12 @@ class RegistrasiState extends State { CustomTextField( title: 'Nama Lengkap', key: Key('Text Field Nama'), + validator: (input) => input.isEmpty ? "*Wajib diisi" : null, ), CustomTextField( title: 'Nomor Telepon', key: Key('Text Field Nomor Telepon'), + validator: (input) => input.isEmpty ? "*Wajib diisi" : null, ), CustomTextField( title: 'Email (Opsional)', @@ -52,10 +55,12 @@ class RegistrasiState extends State { CustomTextField( title: 'Password', key: Key('Text Field Password'), + validator: (input) => input.isEmpty ? "*Wajib diisi" : null, ), CustomTextField( title: 'Konfirmasi Password', key: Key('Text Field Konfirmasi Password'), + validator: (input) => input.isEmpty ? "*Wajib diisi" : null, ), Container( margin: EdgeInsets.fromLTRB(0, 30, 0, 10), -- GitLab