Change all app architecture

This commit is contained in:
Emeline G 2020-07-31 17:30:32 +02:00
parent c902c29684
commit 384c67e9a3
17 changed files with 538 additions and 294 deletions

View File

@ -1,69 +1,7 @@
package fr.astrolabe.astronote_app
import android.content.BroadcastReceiver
import android.os.Bundle
import android.content.Context
import android.content.Intent
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity : FlutterActivity() {
private val CHANNEL = "https://demo.endi.coop"
private val EVENTS = "https://demo.endi.coop/login?nextpage=%2F"
private var startString: String? = null
private var linksReceiver: BroadcastReceiver? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "initialLink") {
if (startString != null) {
result.success(startString)
}
}
}
EventChannel(flutterEngine.dartExecutor, EVENTS).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(args: Any?, events: EventSink) {
linksReceiver = createChangeReceiver(events)
}
override fun onCancel(args: Any?) {
linksReceiver = null
}
}
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = getIntent()
startString = intent.data?.toString()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.action === Intent.ACTION_VIEW) {
linksReceiver?.onReceive(this.applicationContext, intent)
}
}
fun createChangeReceiver(events: EventSink): BroadcastReceiver? {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { // assuming intent.getAction() is Intent.ACTION_VIEW
val dataString = intent.dataString
?: events.error("UNAVAILABLE", "Link unavailable", null)
events.success(dataString)
}
}
}
}

View File

@ -41,9 +41,6 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:demo.endi.coop</string>
</array>
</dict>
</plist>

View File

@ -1,48 +0,0 @@
import 'dart:async';
import 'package:flutter/services.dart';
abstract class Bloc {
void dispose();
}
class DeepLinkBloc extends Bloc {
//Event Channel creation
static const stream =
const EventChannel('https://demo.endi.coop/login?nextpage=%2F');
//Method channel creation
static const platform = const MethodChannel('https://demo.endi.coop');
StreamController<String> _stateController = StreamController();
Stream<String> get state => _stateController.stream;
Sink<String> get stateSink => _stateController.sink;
//Adding the listener into contructor
DeepLinkBloc() {
//Checking application start by deep link
startUri().then(_onRedirected);
//Checking broadcast stream, if deep link was clicked in opened appication
stream.receiveBroadcastStream().listen((d) => _onRedirected(d));
}
_onRedirected(String uri) {
// Throw deep link URI into the BloC's stream
stateSink.add(uri);
}
@override
void dispose() {
_stateController.close();
}
Future<String> startUri() async {
try {
return platform.invokeMethod('initialLink');
} on PlatformException catch (e) {
return "Failed to Invoke: '${e.message}'.";
}
}
}

View File

@ -1,165 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'bloc.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flushbar/flushbar.dart';
class EndiLog extends StatefulWidget {
@override
_EndiLogState createState() => _EndiLogState();
}
class _EndiLogState extends State<EndiLog> {
TextStyle style = TextStyle(fontFamily: 'Varela Round', fontSize: 20.0);
@override
Widget build(BuildContext context) {
DeepLinkBloc _bloc = Provider.of<DeepLinkBloc>(context);
return StreamBuilder<String>(
stream: _bloc.state,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(child: Text('No deep link was used ')));
} else {
return SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(50),
child: Column(
children: <Widget>[
// SizedBox(height: 40),
_logoApp(),
_titleApp(),
SizedBox(height: 80),
_emailField(),
SizedBox(height: 30),
_passwordField(),
SizedBox(height: 50),
_logButton(),
SizedBox(height: 70),
_enDIUrl(),
SizedBox(height: 5),
_astrolabeUrl()
],
),
),
),
);
}
});
}
Widget _logoApp() {
return SizedBox(
height: 100.0,
child: Image.asset(
"assets/astrolabe_logo.jpg",
),
);
}
Widget _titleApp() {
return Text(
"AstroNotes",
style: style.copyWith(color: Colors.black, fontSize: 40),
);
}
Widget _emailField() {
return TextField(
style: style,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
labelText: "E-mail",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
}
Widget _passwordField() {
return TextField(
style: style,
obscureText: true,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
labelText: "Mot de passe",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
}
Widget _logButton() {
return Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(30.0),
color: Color(0xFF4A148C),
child: MaterialButton(
// minWidth: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {},
child: Text("Connexion",
textAlign: TextAlign.center,
style: style.copyWith(
color: Colors.white, fontWeight: FontWeight.bold)),
));
}
void showErrorFlushbar(BuildContext context) {
Flushbar(
message: "Impossible d'ouvrir le lien",
backgroundColor: Colors.red[300],
duration: Duration(seconds: 3),
// Show it with a cascading operator
)..show(context);
}
void _launchLinkEnDI() async {
const url = "https://endi.coop";
if (await canLaunch(url)) {
await launch(url);
} else {
showErrorFlushbar(context);
}
}
void _launchLinkAstrolabe() async {
const url = "https://astrolabe.coop";
if (await canLaunch(url)) {
await launch(url);
} else {
showErrorFlushbar(context);
}
}
Widget _enDIUrl() {
return InkWell(
onTap: _launchLinkEnDI,
child: Text(
"En collaboration avec enDI",
style: style.copyWith(
color: Colors.black,
fontSize: 15,
decoration: TextDecoration.underline),
),
);
}
Widget _astrolabeUrl() {
return InkWell(
onTap: _launchLinkAstrolabe,
child: Text(
"Développé par Astrolabe",
style: style.copyWith(
color: Colors.black,
fontSize: 15,
decoration: TextDecoration.underline),
),
);
}
}

View File

@ -1,22 +1,16 @@
import 'package:astronote_app/screens/sign_in_screen.dart';
import 'package:flutter/material.dart';
import 'bloc.dart';
import 'package:provider/provider.dart';
import 'endi.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
DeepLinkBloc _bloc = DeepLinkBloc();
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Provider<DeepLinkBloc>(
create: (context) => _bloc,
dispose: (context, bloc) => bloc.dispose(),
child: EndiLog())));
theme: ThemeData(
fontFamily: 'Varela Round',
),
home: LoginScreen()
);
}
}

18
lib/models/companies.dart Normal file
View File

@ -0,0 +1,18 @@
class Companies {
int id;
String name;
Companies({this.id, this.name});
Companies.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
return data;
}
}

33
lib/models/datas.dart Normal file
View File

@ -0,0 +1,33 @@
import 'package:astronote_app/models/companies.dart';
class Datas {
String lastname;
List<Companies> companies;
String firstname;
String civilite;
Datas({this.lastname, this.companies, this.firstname, this.civilite});
Datas.fromJson(Map<String, dynamic> json) {
lastname = json['lastname'];
if (json['companies'] != null) {
companies = new List<Companies>();
json['companies'].forEach((v) {
companies.add(new Companies.fromJson(v));
});
}
firstname = json['firstname'];
civilite = json['civilite'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['lastname'] = this.lastname;
if (this.companies != null) {
data['companies'] = this.companies.map((v) => v.toJson()).toList();
}
data['firstname'] = this.firstname;
data['civilite'] = this.civilite;
return data;
}
}

View File

@ -0,0 +1,28 @@
import 'package:astronote_app/models/datas.dart';
class Api {
String status;
String api;
String id;
Datas datas;
Api ({this.status, this.api, this.id, this.datas});
Api.fromJson(Map<String, dynamic> json) {
status = json['status'];
api = json['api'];
id = json['id'];
datas = json['datas'] != null ? new Datas.fromJson(json['datas']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['api'] = this.api;
data['id'] = this.id;
if (this.datas != null) {
data['datas'] = this.datas.toJson();
}
return data;
}
}

View File

@ -0,0 +1,52 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
class SignIn with ChangeNotifier {
String cookie;
Future<void> login(String url, String email, String password) async {
String urlApi = url + '/api/v1/login';
Map<String, String> headers = {
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/json",
};
var body = json.encode({"login": email, "password": password});
final response = await http.post(urlApi, headers: headers, body: body);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(response.headers['set-cookie']);
cookie = response.headers['set-cookie'];
//TODO : return http.get + company_choice_screen root
recoverUserData(urlApi);
// If the server did not return a 200 OK response,
// then throw an exception.
//TODO: error message ?
}
// final responseJson = json.decode(response.body);
}
Future<void> recoverUserData(String url) async {
Map<String, String> headers = {"X-Requested-With": "XMLHttpRequest","Cookie":cookie};
final response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(response.headers['datas']);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
}
// final responseJson = json.decode(response.body);
//
// print(responseJson);
}
}

View File

View File

View File

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'file:///D:/AndroidStudioProjects/astronote_app/lib/widgets/footer_sign_in_widget.dart';
import 'file:///D:/AndroidStudioProjects/astronote_app/lib/widgets/form_sign_in_widget.dart';
import 'package:astronote_app/widgets/header_sign__in_widget.dart';
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
// width: double.infinity,
child: SingleChildScrollView(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.height * 0.07,
right: MediaQuery.of(context).size.height * 0.07),
// scrollDirection: Axis.vertical,
child: Column(
children: <Widget>[
HeaderLogWidget(),
SizedBox(height: MediaQuery.of(context).size.height * 0.08),
FormLogWidget(),
SizedBox(height: MediaQuery.of(context).size.height * 0.08),
FooterLogWidget(),
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
],
),
),
),
);
}
// Widget _enDIUrl() {
// return InkWell(
// onTap: _enDIUrl,
// child: Text(
// "En collaboration avec enDI",
// style: style.copyWith(
// color: Colors.black,
// fontSize: 15.0,
// decoration: TextDecoration.underline),
// ),
// );
// }
//
// Widget _astrolabeUrl() {
// return InkWell(
// onTap: _launchLinkAstrolabe,
// child: Text(
// "Développé par Astrolabe",
// style: style.copyWith(
// color: Colors.black,
// fontSize: 15,
// decoration: TextDecoration.underline),
// ),
// );
// }
//
//
//
// void launchLinkEnDI() async {
// const urlEndi = "hhuh";
//
// if (await canLaunch(urlEndi)) {
// await launch(urlEndi);
// } else {
//
// }
// }
//
// void _launchLinkAstrolabe() async {
// const url = "https://astrolabe.coop";
//
// if (await canLaunch(url)) {
// await launch(url);
// } else {
//
// }
// }
}

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class FooterLogWidget extends StatelessWidget {
final TextStyle style = TextStyle(fontFamily: 'Varela Round', fontSize: 20.0);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
_enDIUrl(),
SizedBox(height: 5),
_astrolabeUrl()
],
),
);
}
Widget _enDIUrl() {
return InkWell(
onTap: _launchLinkEnDI,
child: Text(
"En collaboration avec enDI",
style: style.copyWith(
color: Colors.black,
fontSize: 15.0,
decoration: TextDecoration.underline),
),
);
}
Widget _astrolabeUrl() {
return InkWell(
onTap: _launchLinkAstrolabe,
child: Text(
"Développé par Astrolabe",
style: style.copyWith(
color: Colors.black,
fontSize: 15,
decoration: TextDecoration.underline),
),
);
}
void _launchLinkEnDI() async {
const urlEndi = "https://endi.coop";
if (await canLaunch(urlEndi)) {
await launch(urlEndi);
} else {
//TODO : error message
}
}
void _launchLinkAstrolabe() async {
const url = "https://astrolabe.coop";
if (await canLaunch(url)) {
await launch(url);
} else {
//TODO : error message
}
}
}

View File

@ -0,0 +1,109 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:astronote_app/providers/sign_in.dart';
class FormLogWidget extends StatefulWidget {
@override
_FormLogWidgetState createState() => _FormLogWidgetState();
}
class _FormLogWidgetState extends State<FormLogWidget> {
TextStyle style = TextStyle(fontFamily: 'Varela Round', fontSize: 20.0);
bool _obscurePassword = true;
final GlobalKey<FormState> _formKey = GlobalKey();
bool _isLoading = false;
TextEditingController urlController = new TextEditingController();
TextEditingController emailController = new TextEditingController();
TextEditingController passwordController = new TextEditingController();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: <Widget>[
_urlFormField(),
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
_emailFormField(),
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
_passwordFormField(),
SizedBox(height: MediaQuery.of(context).size.height * 0.07),
_logButton()
],
),
);
}
Widget _urlFormField() {
return TextFormField(
controller: urlController,
style: style,
keyboardType: TextInputType.url,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
labelText: "Lien enDI",
suffixIcon: Icon(Icons.link,color: Colors.indigo[900]),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
}
Widget _emailFormField() {
return TextFormField(
controller: emailController,
style: style,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
labelText: "E-mail",
suffixIcon: Icon(Icons.mail,color: Colors.indigo[900]),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
}
Widget _passwordFormField() {
return TextFormField(
controller: passwordController,
style: style,
obscureText: _obscurePassword,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
labelText: "Mot de passe",
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility : Icons.visibility_off,color: Colors.indigo[900]),
onPressed: () {
setState(() => _obscurePassword = !_obscurePassword);
}),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0))),
);
}
Widget _logButton() {
return Material(
elevation: 3.0,
borderRadius: BorderRadius.circular(30.0),
color: Colors.indigo[900],
child: MaterialButton(
// minWidth: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
setState(() {
_isLoading = true;
});
SignIn().login(urlController.text, emailController.text, passwordController.text);
},
child: Text("Connexion",
textAlign: TextAlign.center,
style: style.copyWith(
color: Colors.white, fontWeight: FontWeight.bold)),
)
);
}
}

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
class HeaderLogWidget extends StatelessWidget {
final TextStyle style = TextStyle(fontFamily: 'Varela Round', fontSize: 20.0);
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.09),
child: Column(
children: <Widget>[_astrolabeLogo(), SizedBox(height: 5.0), _appTitle(context)],
),
),
);
}
Widget _astrolabeLogo() {
return Image.asset(
"assets/astrolabe_logo.jpg",
);
}
Widget _appTitle(BuildContext context) {
return Text("AstroNotes",
style:
style.copyWith(color: Colors.black, fontSize: 40));
}
}

View File

@ -64,6 +64,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.1"
flushbar:
dependency: "direct main"
description:
@ -86,6 +93,20 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
image:
dependency: transitive
description:
@ -93,6 +114,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.12"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.1"
matcher:
dependency: transitive
description:
@ -121,6 +149,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.4"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.0"
petitparser:
dependency: transitive
description:
@ -128,6 +177,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
platform_detect:
dependency: transitive
description:
@ -142,6 +198,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.13"
provider:
dependency: "direct main"
description:
@ -163,6 +226,41 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.8"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+10"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
sky_engine:
dependency: transitive
description: flutter
@ -259,6 +357,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
xml:
dependency: transitive
description:

View File

@ -23,9 +23,12 @@ environment:
dependencies:
flutter:
sdk: flutter
provider: ^4.3.1
flushbar: ^1.10.4
url_launcher: ^5.5.0
flushbar: ^1.10.4
http: ^0.12.2
provider: ^4.3.1
shared_preferences: ^0.5.8
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.