Skip to content
Snippets Groups Projects
Commit b67d77eb authored by Gospodin Bodurov's avatar Gospodin Bodurov
Browse files

Merge branch '3-android-mobile-app-web-view' into 'master'

Mobile app initial implementation.

Closes #3

See merge request !1
parents 75f19bb5 0b8d743e
No related branches found
No related tags found
1 merge request!1Mobile app initial implementation.
import 'package:flutter/material.dart';
import 'package:Vereign/src/app.dart';
import 'config.dart';
void main() {
Config.appFlavor = Flavor.PRODUCTION;
runApp(App());
}
\ No newline at end of file
// app.dart
import 'screens/splashscreen.dart';
import 'package:flutter/material.dart';
import 'screens/home.dart';
import 'package:flutter/services.dart';
import 'dart:async';
import 'dart:developer';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uni_links/uni_links.dart';
class App extends StatelessWidget {
Future<Widget> initApplication() async {
Uri initialUri;
try {
initialUri = await getInitialUri();
} on PlatformException {
initialUri = null;
} on FormatException {
initialUri = null;
}
final prefs = await SharedPreferences.getInstance();
final host = prefs.getString('host');
return new MainApp(initialUri: initialUri, initialHost: host);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Vereign',
theme: ThemeData(
primaryColor: Color(0xFFd51d32),
fontFamily: "Arial",
textTheme: TextTheme(
button: TextStyle(color: Colors.white, fontSize: 18.0),
title: TextStyle(color: Colors.red))),
home: new SplashScreen(
navigateAfterFuture: initApplication,
)
);
}
}
class MainApp extends StatefulWidget {
MainApp({ @required this.initialUri, this.initialHost });
final Uri initialUri;
final String initialHost;
@override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
StreamSubscription _sub;
String _appMode = "";
// Url of the app which invoked OAuth
String _invokerURL;
String _host;
@override
initState() {
super.initState();
initUniLinks();
}
@override
dispose() {
if (_sub != null) _sub.cancel();
super.dispose();
}
Future<Null> initUniLinks() async {
updateAppMode(widget.initialUri);
_sub = getUriLinksStream().listen((Uri uri) {
updateAppMode(uri);
}, onError: (err) {
log('got err: $err');
});
}
updateAppMode(Uri uri) {
if (uri?.path == "/authorize") {
setState(() {
_appMode = "oauth";
_invokerURL = uri.queryParameters["invokerUrl"];
});
} else {
setState(() {
_appMode = "app";
});
}
}
setMode(String mode) {
setState(() {
_appMode = mode;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: Text("Vereign")),
body: Home(
mode: _appMode,
invokerURL: _invokerURL,
setMode: setMode,
host: widget.initialHost
)
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_web_browser/flutter_web_browser.dart';
import 'package:flutter_app_auth_wrapper/flutter_app_auth_wrapper.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:developer';
import 'dart:convert';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config.dart';
class Home extends StatefulWidget {
Home({@required this.mode, @required this.invokerURL, @required this.setMode, @required this.host});
final String mode;
final String invokerURL;
final String host;
final void Function(String) setMode;
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
String _host = Config.appFlavor == Flavor.DEVELOPMENT ? Config.HOSTS[0] : Config.DEFAULT_APP_HOST;
bool _hidden = true;
@override
initState() {
super.initState();
if (widget.host != null) {
setState(() {
_host = widget.host;
});
}
showMode(widget.mode);
FlutterAppAuthWrapper.eventStream().listen((data) {
var token = json.decode(data.toString())["access_token"];
_showAlert(token);
widget.setMode("");
}, onError: (error) {
log("Err $error");
widget.setMode("");
});
Timer(
Duration(seconds: 3),
() {
setState(() {
_hidden = false;
});
}
);
}
Future<void> _showAlert(token) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Authorization success'),
actions: <Widget>[
FlatButton(
child: Text('Go back'),
onPressed: () async {
Navigator.of(context).pop();
log("Open ${widget.invokerURL}");
try {
await launch("${widget.invokerURL}?token=$token&host=$_host");
} catch (e) {
log("Error launching url ${widget.invokerURL}");
}
},
),
],
);
},
);
}
@override
void didUpdateWidget(Home oldWidget) {
// this method IS called when parent widget passes new "props"
// unlike React, this method IS called _before_ the build
// unlike React, this method ISN'T called after setState()
if (widget.host != oldWidget.host && widget.host != null) {
setState(() {
_host = widget.host;
});
}
if (widget.mode != oldWidget.mode) {
showMode(widget.mode);
}
super.didUpdateWidget(oldWidget);
}
showMode(mode) {
if (mode == "app") {
openVereign();
} else if (mode == "oauth") {
startOAuth();
}
}
openVereign() {
FlutterWebBrowser.openWebPage(url: _host, androidToolbarColor: Color(0xFFd51d32));
}
startOAuth() {
var params = Config.getOAuthParams(host: _host);
FlutterAppAuthWrapper.startAuth(
AuthConfig(
clientId: params["clientId"],
clientSecret: params["clientSecret"],
redirectUrl: params["redirectUrl"],
state: "login",
prompt: "consent",
endpoint: AuthEndpoint(
auth: params["authEndpoint"], token: params["tokenEndpoint"]),
scopes: [
"user_account_status",
"user_territory",
"user_profile"
],
),
);
}
@override
Widget build(BuildContext context) {
if (_hidden) {
return Scaffold();
}
var children = <Widget>[
_urlButton(context, "Open Dashboard", openVereign),
_urlButton(context, "Authorize with Vereign", startOAuth)
];
if (Config.appFlavor == Flavor.DEVELOPMENT) {
children.add(
wrapInContainer(
DropdownButton<String>(
value: _host,
onChanged: (String newValue) async {
setState(() {
_host = newValue;
});
final prefs = await SharedPreferences.getInstance();
prefs.setString("host", newValue);
},
items: Config.HOSTS
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.toList(),
)
)
);
}
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children
))));
}
Widget wrapInContainer(Widget widget) {
return Container(
padding: EdgeInsets.all(10.0),
child: widget,
);
}
Widget _urlButton(BuildContext context, String title, listener) {
return wrapInContainer(
FlatButton(
color: Theme.of(context).primaryColor,
textColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 50.0, vertical: 15.0),
child: Text(title),
onPressed: listener
)
);
}
}
\ No newline at end of file
import 'dart:core';
import 'dart:async';
import 'package:flutter/material.dart';
class SplashScreen extends StatefulWidget {
final dynamic navigateAfterFuture;
SplashScreen({this.navigateAfterFuture});
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
load();
}
load() async {
var newWidget = await widget.navigateAfterFuture();
// Show loader additional 2 seconds
Timer(
Duration(seconds: 1),
() {
Navigator.of(context).pushReplacement(new MaterialPageRoute(builder: (BuildContext context) => newWidget));
}
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: new InkWell(
child:new Stack(
fit: StackFit.expand,
children: <Widget>[
new Container(
decoration: new BoxDecoration(
color: Color(0xFFd51d32),
),
),
new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
flex: 2,
child: new Container(
child: new Image.asset('assets/images/vereign_logo_text.png'),
padding: EdgeInsets.all(40)
),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.white),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
),
new Text('Loading', style: new TextStyle(
color: Colors.white
))
],
),
),
],
),
],
),
),
);
}
}
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.10"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.2"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_app_auth_wrapper:
dependency: "direct main"
description:
name: flutter_app_auth_wrapper
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1+3"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.2+1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_browser:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "5d169abf4fb4cb994c26173d5a7a3dd13b6921a2"
url: "git@github.com:markin-io/flutter_web_browser.git"
source: git
version: "0.11.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.3+4"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.5"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.5"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
uni_links:
dependency: "direct main"
description:
name: uni_links
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.16"
sdks:
dart: ">=2.4.0 <3.0.0"
flutter: ">=1.5.0 <2.0.0"
name: Vereign
description: Vereign mobile application.
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
flutter_web_browser:
# Use
git: git@github.com:markin-io/flutter_web_browser.git
uni_links: 0.2.0
flutter_app_auth_wrapper: ^0.1.1+3
url_launcher: 5.1.0
shared_preferences: 0.5.3+4
dev_dependencies:
flutter_test:
sdk: flutter
flutter_launcher_icons: "^0.7.2"
flutter_icons:
android: true
ios: true
image_path: "assets/images/vereign_icon.png"
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/vereign_logo_text.png
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment