Jonatas

added GetConnect

Showing 70 changed files with 2739 additions and 40 deletions
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
... ...
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<application
android:label="example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
... ...
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
... ...
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
... ...
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
... ...
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
... ...
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
... ...
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
... ...
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'routes/app_pages.dart';
... ...
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import '../data/home_api_provider.dart';
import '../data/home_repository.dart';
import '../domain/adapters/repository_adapter.dart';
... ... @@ -8,8 +8,8 @@ import '../presentation/controllers/home_controller.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => Dio());
Get.lazyPut<IHomeRepository>(() => HomeRepository(dio: Get.find()));
Get.lazyPut<IHomeProvider>(() => HomeProvider());
Get.lazyPut<IHomeRepository>(() => HomeRepository(provider: Get.find()));
Get.lazyPut(() => HomeController(homeRepository: Get.find()));
}
}
... ...
import 'package:get/get.dart';
import '../domain/entity/cases_model.dart';
// ignore: one_member_abstracts
abstract class IHomeProvider {
Future<Response<CasesModel>> getCases(String path);
}
class HomeProvider extends GetConnect implements IHomeProvider {
@override
void onInit() {
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
}
... ...
import 'package:dio/dio.dart';
import '../domain/adapters/repository_adapter.dart';
import '../domain/entity/cases_model.dart';
import 'home_api_provider.dart';
class HomeRepository implements IHomeRepository {
HomeRepository({this.dio});
final Dio dio;
HomeRepository({this.provider});
final IHomeProvider provider;
@override
Future<CasesModel> getCases() async {
try {
final response = await dio.get("https://api.covid19api.com/summary");
return CasesModel.fromJson(response.data as Map<String, dynamic>);
} on Exception catch (e) {
print(e.toString());
return Future.error(e.toString());
final cases = await provider.getCases("/summary");
if (cases.status.hasError) {
return Future.error(cases.statusText);
} else {
return cases.body;
}
}
}
... ...
... ... @@ -15,12 +15,12 @@ class CasesModel {
this.date,
});
factory CasesModel.fromRawJson(String str) =>
static CasesModel fromRawJson(String str) =>
CasesModel.fromJson(json.decode(str) as Map<String, dynamic>);
String toRawJson() => json.encode(toJson());
factory CasesModel.fromJson(Map<String, dynamic> json) => CasesModel(
static CasesModel fromJson(dynamic json) => CasesModel(
global: json["Global"] == null
? null
: Global.fromJson(json["Global"] as Map<String, dynamic>),
... ...
flutter/ephemeral
... ...
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "example")
set(APPLICATION_ID "com.example.example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
... ...
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
PkgConfig::BLKID
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)
... ...
//
// Generated file. Do not edit.
//
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}
... ...
//
// Generated file. Do not edit.
//
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
... ...
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
... ...
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}
... ...
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen *screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "example");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
}
else {
gtk_window_set_title(window, "example");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GObject::dispose.
static void my_application_dispose(GObject *object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
nullptr));
}
... ...
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_
... ...
... ... @@ -28,7 +28,6 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
get:
path: ../
dio: ^3.0.9
get_test: ^3.13.3
dependency_overrides:
... ...
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
Fore more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
-->
<base href="/">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
... ...
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
... ...
... ... @@ -3,6 +3,7 @@
/// injection, and route management in a quick and practical way.
library get;
export 'get_connect/connect.dart';
export 'get_core/get_core.dart';
export 'get_instance/get_instance.dart';
export 'get_navigation/get_navigation.dart';
... ...
import '../get_instance/src/lifecycle.dart';
import 'http/src/certificates/certificates.dart';
import 'http/src/http.dart';
import 'http/src/response/response.dart';
import 'sockets/sockets.dart';
export 'http/src/certificates/certificates.dart';
export 'http/src/http.dart';
export 'http/src/multipart/form_data.dart';
export 'http/src/multipart/multipart_file.dart';
export 'http/src/response/response.dart';
export 'sockets/sockets.dart';
abstract class GetConnectInterface with GetLifeCycleBase {
List<GetSocket> sockets;
GetHttpClient get httpClient;
Future<Response<T>> get<T>(
String url, {
Map<String, String> headers,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
});
Future<Response<T>> post<T>(
String url,
dynamic body, {
String contentType,
Map<String, String> headers,
Map<String, dynamic> query,
});
Future<Response<T>> put<T>(
String url,
Map<String, dynamic> body, {
String contentType,
Map<String, String> headers,
Map<String, dynamic> query,
});
Future<Response<T>> delete<T>(
String url, {
Map<String, String> headers,
String contentType,
Map<String, dynamic> query,
});
GetSocket socket(String url, {Duration ping = const Duration(seconds: 5)});
}
class GetConnect extends GetConnectInterface {
GetConnect({
this.userAgent = 'getx-client',
this.timeout = const Duration(seconds: 5),
this.followRedirects = true,
this.maxRedirects = 5,
this.maxAuthRetries = 1,
this.allowAutoSignedCert = false,
}) {
$configureLifeCycle();
}
bool allowAutoSignedCert;
String userAgent;
String baseUrl;
String defaultContentType = 'application/json; charset=utf-8';
bool followRedirects;
int maxRedirects;
int maxAuthRetries;
Decoder defaultDecoder;
Duration timeout;
List<TrustedCertificate> trustedCertificates;
GetHttpClient _httpClient;
List<GetSocket> _sockets;
@override
List<GetSocket> get sockets => _sockets ??= <GetSocket>[];
@override
GetHttpClient get httpClient => _httpClient ??= GetHttpClient(
userAgent: userAgent,
timeout: timeout,
followRedirects: followRedirects,
maxRedirects: maxRedirects,
maxAuthRetries: maxAuthRetries,
allowAutoSignedCert: allowAutoSignedCert,
baseUrl: baseUrl,
trustedCertificates: trustedCertificates,
);
@override
Future<Response<T>> get<T>(
String url, {
Map<String, String> headers,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
}) {
_checkIfDisposed();
return httpClient.get(
url,
headers: headers,
contentType: contentType,
query: query,
decoder: decoder,
);
}
@override
Future<Response<T>> post<T>(
String url,
dynamic body, {
String contentType,
Map<String, String> headers,
Map<String, dynamic> query,
Decoder<T> decoder,
}) {
_checkIfDisposed();
return httpClient.post<T>(
url,
body,
headers: headers,
contentType: contentType,
query: query,
decoder: decoder,
);
}
@override
Future<Response<T>> put<T>(
String url,
Map<String, dynamic> body, {
String contentType,
Map<String, String> headers,
Map<String, dynamic> query,
Decoder<T> decoder,
}) {
_checkIfDisposed();
return httpClient.put(
url,
body,
headers: headers,
contentType: contentType,
query: query,
decoder: decoder,
);
}
@override
Future<Response<T>> delete<T>(
String url, {
Map<String, String> headers,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
}) {
_checkIfDisposed();
return httpClient.delete(
url,
headers: headers,
contentType: contentType,
query: query,
decoder: decoder,
);
}
@override
GetSocket socket(String url, {Duration ping = const Duration(seconds: 5)}) {
_checkIfDisposed(isHttp: false);
final _url = baseUrl == null ? url : baseUrl + url;
final _socket = GetSocket(_url, ping: ping);
sockets.add(_socket);
return _socket;
}
bool _isDisposed = false;
bool get isDisposed => _isDisposed;
void _checkIfDisposed({bool isHttp = true}) {
if (_isDisposed) {
throw 'Can not emit events to disposed clients';
}
}
void dispose() {
if (_sockets != null) {
for (var socket in sockets) {
socket.close();
}
_sockets?.clear();
sockets = null;
}
if (_httpClient != null) {
httpClient.close();
_httpClient = null;
}
_isDisposed = true;
}
}
... ...
class TrustedCertificate {
final List<int> bytes;
TrustedCertificate(this.bytes);
}
... ...
class GetHttpException implements Exception {
final String message;
final Uri uri;
GetHttpException(this.message, [this.uri]);
@override
String toString() => message;
}
class UnauthorizedException implements Exception {
@override
String toString() {
return 'Operation Unauthorized';
}
}
class UnexpectedFormat implements Exception {
final String message;
UnexpectedFormat(this.message);
@override
String toString() {
return 'Unexpected format: $message';
}
}
... ...
import 'dart:async';
import 'dart:convert';
import 'package:meta/meta.dart';
import '../src/certificates/certificates.dart';
import '../src/exceptions/exceptions.dart';
import '../src/http_impl/http_request_stub.dart'
if (dart.library.html) 'http_impl/http_request_html.dart'
if (dart.library.io) 'http_impl/http_request_io.dart';
import '../src/http_impl/request_base.dart';
import '../src/interceptors/get_interceptors.dart';
import '../src/multipart/form_data.dart';
import '../src/request/request.dart';
import '../src/response/response.dart';
import '../src/status/http_status.dart';
typedef Decoder<T> = T Function(dynamic data);
class GetHttpClient {
String userAgent;
String baseUrl;
String defaultContentType = 'application/json; charset=utf-8';
bool followRedirects;
int maxRedirects;
int maxAuthRetries;
Decoder defaultDecoder;
Duration timeout;
final HttpRequestBase _httpClient;
final GetModifier _interceptor;
GetHttpClient({
this.userAgent = 'getx-client',
this.timeout = const Duration(seconds: 8),
this.followRedirects = true,
this.maxRedirects = 5,
this.maxAuthRetries = 1,
bool allowAutoSignedCert = false,
this.baseUrl,
List<TrustedCertificate> trustedCertificates,
}) : _httpClient = HttpRequestImpl(
allowAutoSignedCert: allowAutoSignedCert,
trustedCertificates: trustedCertificates,
),
_interceptor = GetModifier();
Uri _createUri(String url, Map<String, dynamic> query) {
if (baseUrl != null) {
url = baseUrl + url;
}
final uri = Uri.parse(url);
if (query != null) {
uri.replace(queryParameters: query);
}
return uri;
}
Future<Request<T>> _requestWithBody<T>(
String url,
String contentType,
dynamic body,
String method,
Map<String, dynamic> query,
Decoder<T> decoder,
) async {
assert(method != null);
assert(body != null);
List<int> bodyBytes;
if (body is FormData) {
bodyBytes = await body.readAsBytes();
} else {
try {
var jsonString = json.encode(body);
//TODO check this implementation
if (contentType != null) {
if (contentType.toLowerCase() ==
'application/x-www-form-urlencoded') {
var paramName = 'param';
jsonString = '$paramName=${Uri.encodeQueryComponent(jsonString)}';
}
}
bodyBytes = utf8.encode(jsonString);
} on Exception catch (err) {
throw UnexpectedFormat(err.toString());
}
}
final bodyStream = BodyBytes.fromBytes(bodyBytes);
final headers = <String, String>{};
_setHeadersWithBody(contentType, headers, bodyBytes);
final uri = _createUri(url, query);
return Request(
method: method,
url: uri,
headers: headers,
bodyBytes: bodyStream,
followRedirects: followRedirects,
maxRedirects: maxRedirects,
);
}
void _setHeadersWithBody(
String contentType,
// String jsonString,
Map<String, String> headers,
List<int> bodyBytes,
// List<MultipartFile> files,
) {
// if (files != null) {
// headers['content-type'] = 'multipart/form-data';
// headers['x-requested-with'] = 'XMLHttpRequest';
// } else {
// headers['content-type'] = contentType ?? defaultContentType;
// }
headers['content-type'] =
contentType ?? defaultContentType; // verify if this is better location
headers['user-agent'] = userAgent;
headers['content-length'] = bodyBytes.length.toString();
}
void _setSimpleHeaders(
Map<String, String> headers,
String contentType,
) {
headers['content-type'] = contentType ?? defaultContentType;
headers['user-agent'] = userAgent;
}
Future<Response<T>> _performRequest<T>(
HandlerExecute<T> handler, {
bool authenticate = false,
int requestNumber = 1,
Map<String, String> headers,
}) async {
try {
var request = await handler();
headers?.forEach((key, value) {
request.headers[key] = value;
});
if (authenticate) await _interceptor.authenticator(request);
await _interceptor.modifyRequest(request);
var response = await _httpClient.send<T>(request);
await _interceptor.modifyResponse(request, response);
if (HttpStatus.unauthorized == response.statusCode &&
_interceptor.authenticator != null &&
requestNumber <= maxAuthRetries) {
return _performRequest(
handler,
authenticate: true,
requestNumber: requestNumber + 1,
headers: request.headers,
);
} else if (HttpStatus.unauthorized == response.statusCode) {
throw UnauthorizedException();
}
return response;
} on Exception catch (err) {
throw GetHttpException(err.toString());
}
}
Future<Request<T>> _get<T>(
String url,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
) {
final headers = <String, String>{};
_setSimpleHeaders(headers, contentType);
final uri = _createUri(url, query);
return Future.value(Request<T>(
method: 'get',
url: uri,
headers: headers,
decoder: decoder ?? (defaultDecoder as Decoder<T>),
));
}
Future<Request<T>> _post<T>(
String url, {
String contentType,
@required dynamic body,
Map<String, dynamic> query,
Decoder<T> decoder,
// List<MultipartFile> files,
}) {
assert(body != null);
return _requestWithBody<T>(
url,
contentType,
body,
'post',
query,
decoder,
);
}
Future<Request<T>> _put<T>(
String url, {
String contentType,
@required dynamic body,
@required Map<String, dynamic> query,
Decoder<T> decoder,
// List<MultipartFile> files,
}) {
assert(body != null);
return _requestWithBody(url, contentType, body, 'put', query, decoder);
}
Request<T> _delete<T>(
String url,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
) {
final headers = <String, String>{};
_setSimpleHeaders(headers, contentType);
final uri = _createUri(url, query);
return Request<T>(
method: 'delete', url: uri, headers: headers, decoder: decoder);
}
Future<Response<T>> post<T>(
String url,
dynamic body, {
String contentType,
Map<String, String> headers,
Map<String, dynamic> query,
Decoder<T> decoder,
// List<MultipartFile> files,
}) async {
try {
var response = await _performRequest<T>(
() => _post<T>(
url,
contentType: contentType,
body: body,
query: query,
decoder: decoder,
// files: files,
),
headers: headers,
);
return response;
} on Exception catch (e) {
return Future.value(Response<T>(
request: null,
statusCode: null,
body: null,
statusText: 'Can not connect to server. Reason: $e',
));
}
}
Future<Response<T>> put<T>(
String url,
Map<String, dynamic> body, {
String contentType,
Map<String, String> headers,
Map<String, dynamic> query,
Decoder<T> decoder,
}) async {
try {
var response = await _performRequest(
() => _put(
url,
contentType: contentType,
query: query,
body: body,
decoder: decoder,
),
headers: headers,
);
return response;
} on Exception catch (e) {
return Future.value(Response<T>(
request: null,
statusCode: null,
body: null,
statusText: 'Can not connect to server. Reason: $e',
));
}
}
Future<Response<T>> get<T>(
String url, {
Map<String, String> headers,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
}) async {
try {
var response = await _performRequest<T>(
() => _get<T>(url, contentType, query, decoder),
headers: headers,
);
return response;
} on Exception catch (e) {
return Future.value(Response<T>(
request: null,
statusCode: null,
body: null,
statusText: 'Can not connect to server. Reason: $e',
));
}
}
Future<Response<T>> delete<T>(
String url, {
Map<String, String> headers,
String contentType,
Map<String, dynamic> query,
Decoder<T> decoder,
}) async {
try {
var response = await _performRequest(
() async => _delete<T>(url, contentType, query, decoder),
headers: headers,
);
return response;
} on Exception catch (e) {
return Future.value(Response<T>(
request: null,
statusCode: null,
body: null,
statusText: 'Can not connect to server. Reason: $e',
));
}
}
void close() {
_httpClient.close();
}
}
... ...
import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;
import 'dart:typed_data';
import '../certificates/certificates.dart';
import '../exceptions/exceptions.dart';
import '../request/request.dart';
import '../response/response.dart';
import 'request_base.dart';
/// A `dart:html` implementation of `HttpRequestBase`.
class HttpRequestImpl implements HttpRequestBase {
HttpRequestImpl({
bool allowAutoSignedCert = true,
List<TrustedCertificate> trustedCertificates,
});
/// The currently active XHRs.
final _xhrs = <html.HttpRequest>{};
///This option requires that you submit credentials for requests
///on different sites. The default is false
bool withCredentials = false;
/// Sends an HTTP request and asynchronously returns the response.
@override
Future<Response<T>> send<T>(Request<T> request) async {
var bytes = await request.bodyBytes.toBytes();
html.HttpRequest xhr;
// if (request.files != null) {
// var data = html.FormData();
// if (request.files != null) {
// for (MultipartFile element in request.files) {
// var stream = element.finalize();
// data.appendBlob(element., html.File(element.finalize(),
// element.filename),
// element.filename);
// }
// }
// xhr = await html.HttpRequest.request('${request.url}',
// method: request.method, sendData: data);
// } else {
// xhr = html.HttpRequest()
// ..open(request.method, '${request.url}', async: true);
// }
xhr = html.HttpRequest()
..open(request.method, '${request.url}', async: true); // check this
_xhrs.add(xhr);
xhr
..responseType = 'blob'
..withCredentials = withCredentials;
request.headers.forEach(xhr.setRequestHeader);
var completer = Completer<Response<T>>();
xhr.onLoad.first.then((_) {
var blob = xhr.response as html.Blob ?? html.Blob([]);
var reader = html.FileReader();
reader.onLoad.first.then((_) async {
var bodyBytes = BodyBytes.fromBytes(reader.result as Uint8List);
final stringBody =
await bodyBytesToString(bodyBytes, xhr.responseHeaders);
T body;
try {
if (request.decoder == null) {
body = jsonDecode(stringBody) as T;
} else {
body = request.decoder(jsonDecode(stringBody));
}
// body = request.decoder(stringBody);
} on Exception catch (_) {
body = stringBody as T;
}
// final body = jsonDecode(stringBody);
final response = Response<T>(
bodyBytes: bodyBytes,
statusCode: xhr.status,
request: request,
headers: xhr.responseHeaders,
statusText: xhr.statusText,
body: body,
);
completer.complete(response);
});
reader.onError.first.then((error) {
completer.completeError(
GetHttpException(error.toString(), request.url),
StackTrace.current,
);
});
reader.readAsArrayBuffer(blob);
});
xhr.onError.first.then((_) {
completer.completeError(
GetHttpException('XMLHttpRequest error.', request.url),
StackTrace.current);
});
xhr.send(bytes);
try {
return await completer.future;
} finally {
_xhrs.remove(xhr);
}
}
/// Closes the client and abort all active requests.
@override
void close() {
for (var xhr in _xhrs) {
xhr.abort();
}
}
}
... ...
import 'dart:convert';
import 'dart:io' as io;
import '../certificates/certificates.dart';
import '../exceptions/exceptions.dart';
import '../request/request.dart';
import '../response/response.dart';
import 'request_base.dart';
/// A `dart:io` implementation of `HttpRequestBase`.
class HttpRequestImpl extends HttpRequestBase {
io.HttpClient _httpClient;
io.SecurityContext _securityContext;
HttpRequestImpl({
bool allowAutoSignedCert = true,
List<TrustedCertificate> trustedCertificates,
}) {
_httpClient = io.HttpClient();
if (trustedCertificates != null) {
_securityContext = io.SecurityContext();
for (final trustedCertificate in trustedCertificates) {
_securityContext
.setTrustedCertificatesBytes(List.from(trustedCertificate.bytes));
}
}
_httpClient = io.HttpClient(context: _securityContext);
_httpClient.badCertificateCallback = (_, __, ___) => allowAutoSignedCert;
}
@override
Future<Response<T>> send<T>(Request<T> request) async {
var requestBody = await request.bodyBytes.toBytes();
var stream = BodyBytes.fromBytes(requestBody ?? const []);
try {
var ioRequest = (await _httpClient.openUrl(request.method, request.url))
..followRedirects = request.followRedirects
..persistentConnection = request.persistentConnection
..maxRedirects = request.maxRedirects
..contentLength = requestBody.length ?? -1;
request.headers.forEach(ioRequest.headers.set);
var response = await stream.pipe(ioRequest) as io.HttpClientResponse;
var headers = <String, String>{};
response.headers.forEach((key, values) {
headers[key] = values.join(',');
});
final bodyBytes = BodyBytes(response);
final stringBody = await bodyBytesToString(bodyBytes, headers);
T body;
try {
if (request.decoder == null) {
body = jsonDecode(stringBody) as T;
} else {
body = request.decoder(jsonDecode(stringBody));
}
} on Exception catch (_) {
body = stringBody as T;
}
return Response(
headers: headers,
request: request,
statusCode: response.statusCode,
statusText: response.reasonPhrase,
bodyBytes: bodyBytes,
body: body,
);
} on io.HttpException catch (error) {
throw GetHttpException(error.message, error.uri);
}
}
/// Closes the HttpClient.
@override
void close() {
if (_httpClient != null) {
_httpClient.close(force: true);
_httpClient = null;
}
}
}
extension FileExt on io.FileSystemEntity {
String get fileName {
return this?.path?.split(io.Platform.pathSeparator)?.last;
}
}
... ...
import '../certificates/certificates.dart';
import '../request/request.dart';
import '../response/response.dart';
import 'request_base.dart';
class HttpRequestImpl extends HttpRequestBase {
HttpRequestImpl({
bool allowAutoSignedCert = true,
List<TrustedCertificate> trustedCertificates,
});
@override
void close() {}
@override
Future<Response<T>> send<T>(Request<T> request) {
throw UnimplementedError();
}
}
... ...
import '../request/request.dart';
import '../response/response.dart';
/// Abstract interface of [HttpRequestImpl].
abstract class HttpRequestBase {
/// Sends an HTTP [Request].
Future<Response<T>> send<T>(Request<T> request);
/// Closes the [Request] and cleans up any resources associated with it.
void close();
}
... ...
import '../request/request.dart';
import '../response/response.dart';
typedef RequestModifier = Future<Request> Function(Request request);
typedef ResponseModifier = Future<Null> Function(
Request request, Response response);
typedef HandlerExecute<T> = Future<Request<T>> Function();
class GetModifier {
final _requestModifiers = <RequestModifier>[];
final _responseModifiers = <ResponseModifier>[];
RequestModifier authenticator;
void addRequestModifier(RequestModifier interceptor) {
_requestModifiers.add(interceptor);
}
void removeRequestModifier(RequestModifier interceptor) {
_requestModifiers.remove(interceptor);
}
void addResponseModifier(ResponseModifier interceptor) {
_responseModifiers.add(interceptor);
}
void removeResponseModifier(ResponseModifier interceptor) {
_requestModifiers.remove(interceptor);
}
Future<void> modifyRequest(Request request) async {
if (_requestModifiers.isNotEmpty) {
for (var interceptor in _requestModifiers) {
await interceptor(request);
}
}
}
Future<void> modifyResponse(Request request, Response response) async {
if (_responseModifiers.isNotEmpty) {
for (var interceptor in _responseModifiers) {
await interceptor(request, response);
}
}
}
}
... ...
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import '../../../../get_rx/src/rx_stream/rx_stream.dart';
import '../utils/utils.dart';
import 'multipart_file.dart';
class FormData {
static const _BOUNDARY_LENGTH = GET_BOUNDARY.length + 10;
final String boundary;
/// The boundary of FormData, it consists of a constant prefix and a random
/// postfix to assure the the boundary unpredictable and unique, each FormData
/// instance will be different.
final _newlineRegExp = RegExp(r'\r\n|\r|\n');
/// The form fields to send for this request.
final fields = <MapEntry<String, String>>[];
/// The [files].
final files = <MapEntry<String, MultipartFile>>[];
/// Whether [finalize] has been called.
bool get isFinalized => _isFinalized;
bool _isFinalized = false;
FormData(Map<String, dynamic> map)
: boundary = GET_BOUNDARY +
Random().nextInt(4294967296).toString().padLeft(10, '0') {
encodeMap(
map,
(key, value) {
if (value == null) return null;
if (value is MultipartFile) {
files.add(MapEntry(key, value));
} else {
fields.add(MapEntry(key, value.toString()));
}
return null;
},
encode: false,
);
}
/// Returns the header string for a field. The return value is guaranteed to
/// contain only ASCII characters.
String _headerForField(String name, String value) {
var header =
'content-disposition: form-data; name="${_browserEncode(name)}"';
if (!isPlainAscii(value)) {
header = '$header\r\n'
'content-type: text/plain; charset=utf-8\r\n'
'content-transfer-encoding: binary';
}
return '$header\r\n\r\n';
}
/// Returns the header string for a file. The return value is guaranteed to
/// contain only ASCII characters.
String _headerForFile(MapEntry<String, MultipartFile> entry) {
var file = entry.value;
var header =
'content-disposition: form-data; name="${_browserEncode(entry.key)}"';
if (file.filename != null) {
header = '$header; filename="${_browserEncode(file.filename)}"';
}
header = '$header\r\n'
'content-type: ${file.contentType}';
return '$header\r\n\r\n';
}
/// Encode [value] in the same way browsers do.
String _browserEncode(String value) {
// http://tools.ietf.org/html/rfc2388 mandates some complex encodings for
// field names and file names, but in practice user agents seem not to
// follow this at all. Instead, they URL-encode `\r`, `\n`, and `\r\n` as
// `\r\n`; URL-encode `"`; and do nothing else (even for `%` or non-ASCII
// characters). We follow their behavior.
return value.replaceAll(_newlineRegExp, '%0D%0A').replaceAll('"', '%22');
}
/// The total length of the request body, in bytes. This is calculated from
/// [fields] and [files] and cannot be set manually.
int get length {
var length = 0;
for (final item in fields) {
length += '--'.length +
_BOUNDARY_LENGTH +
'\r\n'.length +
utf8.encode(_headerForField(item.key, item.value)).length +
utf8.encode(item.value).length +
'\r\n'.length;
}
for (var file in files) {
length += '--'.length +
_BOUNDARY_LENGTH +
'\r\n'.length +
utf8.encode(_headerForFile(file)).length +
file.value.length +
'\r\n'.length;
}
return length + '--'.length + _BOUNDARY_LENGTH + '--\r\n'.length;
}
Stream<List<int>> finalize() {
if (isFinalized) {
throw StateError("Can't finalize a finalized MultipartFile.");
}
_isFinalized = true;
final getStream = GetStream<List<int>>();
for (final item in fields) {
stringToBytes('--$boundary\r\n', getStream);
stringToBytes(_headerForField(item.key, item.value), getStream);
stringToBytes(item.value, getStream);
writeLine(getStream);
}
Future.forEach<MapEntry<String, MultipartFile>>(files, (file) {
stringToBytes('--$boundary\r\n', getStream);
stringToBytes(_headerForFile(file), getStream);
return streamToFuture(file.value.stream, getStream)
.then((_) => writeLine(getStream));
}).then((_) {
stringToBytes('--$boundary--\r\n', getStream);
getStream.close();
});
return getStream.stream;
}
///Transform the entire FormData contents as a list of bytes asynchronously.
Future<List<int>> readAsBytes() {
return Future(() => finalize().reduce((a, b) => [...a, ...b]));
}
}
... ...
class MultipartFile {
final String contentType;
final Stream<List<int>> _stream;
final int length;
final String filename;
MultipartFile(
List<int> bytes, {
this.filename,
this.contentType = 'application/octet-stream',
}) : length = bytes.length,
_stream = Stream.fromIterable([bytes]);
Stream<List<int>> get stream => _stream;
}
... ...
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:meta/meta.dart';
import '../http.dart';
import '../multipart/form_data.dart';
class Request<T> {
/// Headers attach to this [Request]
final Map<String, String> headers;
/// The [Uri] from request
final Uri url;
final Decoder<T> decoder;
/// The Http Method from this [Request]
/// ex: `GET`,`POST`,`PUT`,`DELETE`
final String method;
/// The BodyBytes of body from this [Request]
final BodyBytes bodyBytes;
/// When true, the client will follow redirects to resolves this [Request]
final bool followRedirects;
/// The maximum number of redirects if [followRedirects] is true.
final int maxRedirects;
final bool persistentConnection;
final FormData files;
const Request._({
@required this.method,
@required this.bodyBytes,
@required this.url,
@required this.headers,
@required this.followRedirects,
@required this.maxRedirects,
@required this.files,
@required this.persistentConnection,
@required this.decoder,
});
factory Request({
@required Uri url,
@required String method,
@required Map<String, String> headers,
BodyBytes bodyBytes,
bool followRedirects = true,
int maxRedirects = 4,
FormData files,
bool persistentConnection = true,
final Decoder<T> decoder,
}) {
assert(url != null);
assert(method != null);
assert(followRedirects != null);
if (followRedirects) {
assert(maxRedirects != null);
assert(maxRedirects > 0);
}
return Request._(
url: url,
method: method,
bodyBytes: bodyBytes ??= BodyBytes.fromBytes(const []),
headers: Map.from(headers ??= <String, String>{}),
followRedirects: followRedirects,
maxRedirects: maxRedirects,
files: files,
persistentConnection: persistentConnection,
decoder: decoder,
);
}
/// Constructs a [Request] containing the same properties as [request].
factory Request.copyWith(Request request) => Request(
method: request.method,
url: request.url,
headers: request.headers,
bodyBytes: request.bodyBytes,
followRedirects: request.followRedirects,
maxRedirects: request.maxRedirects,
files: request.files,
persistentConnection: request.persistentConnection,
);
}
class BodyBytes extends StreamView<List<int>> {
BodyBytes(Stream<List<int>> stream) : super(stream);
factory BodyBytes.fromBytes(List<int> bytes) =>
BodyBytes(Stream.fromIterable([bytes]));
Future<Uint8List> toBytes() {
var completer = Completer<Uint8List>();
var sink = ByteConversionSink.withCallback(
(bytes) => completer.complete(
Uint8List.fromList(bytes),
),
);
listen(sink.add,
onError: completer.completeError,
onDone: sink.close,
cancelOnError: true);
return completer.future;
}
Future<String> bytesToString([Encoding encoding = utf8]) =>
encoding.decodeStream(this);
Stream<String> toStringStream([Encoding encoding = utf8]) =>
encoding.decoder.bind(this);
}
... ...
import 'dart:collection';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import '../request/request.dart';
import '../status/http_status.dart';
class Response<T> {
const Response({
@required this.request,
@required this.statusCode,
// ignore: always_require_non_null_named_parameters
this.bodyBytes,
this.statusText = '',
this.headers = const {},
@required this.body,
});
/// The Http [Request] linked with this [Response].
final Request request;
/// The response headers.
final Map<String, String> headers;
/// The status code returned by the server.
final int statusCode;
/// Human-readable context for [statusCode].
final String statusText;
/// [HttpStatus] from [Response]. `status.connectionError` is true
/// when statusCode is null. `status.isUnauthorized` is true when
/// statusCode is equal `401`. `status.isNotFound` is true when
/// statusCode is equal `404`. `status.isServerError` is true when
/// statusCode is between `500` and `599`.
HttpStatus get status => HttpStatus(statusCode);
/// `hasError` is true when statusCode is not between 200 and 299.
bool get hasError => status.hasError;
/// `isOk` is true when statusCode is between 200 and 299.
bool get isOk => !hasError;
/// `unauthorized` is true when statusCode is equal `401`.
bool get unauthorized => status.isUnauthorized;
/// The response body as a Stream of Bytes.
final BodyBytes bodyBytes;
/// The decoded body of this [Response]. You can access the
/// body parameters as Map
/// Ex: body['title'];
final T body;
}
Future<String> bodyBytesToString(
BodyBytes bodyBytes, Map<String, String> headers) {
return bodyBytes.bytesToString(_encodingForHeaders(headers));
}
/// Returns the encoding to use for a response with the given headers.
///
/// Defaults to [latin1] if the headers don't specify a charset or if that
/// charset is unknown.
Encoding _encodingForHeaders(Map<String, String> headers) =>
_encodingForCharset(_contentTypeForHeaders(headers).parameters['charset']);
/// Returns the [Encoding] that corresponds to [charset].
///
/// Returns [fallback] if [charset] is null or if no [Encoding] was found that
/// corresponds to [charset].
Encoding _encodingForCharset(String charset, [Encoding fallback = latin1]) {
if (charset == null) return fallback;
return Encoding.getByName(charset) ?? fallback;
}
/// Returns the [MediaType] object for the given headers's content-type.
///
/// Defaults to `application/octet-stream`.
HeaderValue _contentTypeForHeaders(Map<String, String> headers) {
var contentType = headers['content-type'];
if (contentType != null) return HeaderValue.parse(contentType);
return HeaderValue('application/octet-stream');
}
class HeaderValue {
String _value;
Map<String, String> _parameters;
Map<String, String> _unmodifiableParameters;
HeaderValue([this._value = '', Map<String, String> parameters]) {
if (parameters != null) {
_parameters = HashMap<String, String>.from(parameters);
}
}
static HeaderValue parse(String value,
{String parameterSeparator = ';',
String valueSeparator,
bool preserveBackslash = false}) {
var result = HeaderValue();
result._parse(value, parameterSeparator, valueSeparator, preserveBackslash);
return result;
}
String get value => _value;
void _ensureParameters() {
_parameters ??= HashMap<String, String>();
}
Map<String, String> get parameters {
_ensureParameters();
_unmodifiableParameters ??= UnmodifiableMapView(_parameters);
return _unmodifiableParameters;
}
@override
String toString() {
var sb = StringBuffer();
sb.write(_value);
if (parameters != null && parameters.isNotEmpty) {
_parameters.forEach((name, value) {
sb..write('; ')..write(name)..write('=')..write(value);
});
}
return sb.toString();
}
void _parse(String value, String parameterSeparator, String valueSeparator,
bool preserveBackslash) {
var index = 0;
bool done() => index == value.length;
void skipWS() {
while (!done()) {
if (value[index] != ' ' && value[index] != '\t') return;
index++;
}
}
String parseValue() {
var start = index;
while (!done()) {
if (value[index] == ' ' ||
value[index] == '\t' ||
value[index] == valueSeparator ||
value[index] == parameterSeparator) {
break;
}
index++;
}
return value.substring(start, index);
}
void expect(String expected) {
if (done() || value[index] != expected) {
throw StateError('Failed to parse header value');
}
index++;
}
void maybeExpect(String expected) {
if (value[index] == expected) index++;
}
void parseParameters() {
var parameters = HashMap<String, String>();
_parameters = UnmodifiableMapView(parameters);
String parseParameterName() {
var start = index;
while (!done()) {
if (value[index] == ' ' ||
value[index] == '\t' ||
value[index] == '=' ||
value[index] == parameterSeparator ||
value[index] == valueSeparator) {
break;
}
index++;
}
return value.substring(start, index).toLowerCase();
}
String parseParameterValue() {
if (!done() && value[index] == '\"') {
var sb = StringBuffer();
index++;
while (!done()) {
if (value[index] == '\\') {
if (index + 1 == value.length) {
throw StateError('Failed to parse header value');
}
if (preserveBackslash && value[index + 1] != '\"') {
sb.write(value[index]);
}
index++;
} else if (value[index] == '\"') {
index++;
break;
}
sb.write(value[index]);
index++;
}
return sb.toString();
} else {
var val = parseValue();
return val == '' ? null : val;
}
}
while (!done()) {
skipWS();
if (done()) return;
var name = parseParameterName();
skipWS();
if (done()) {
parameters[name] = null;
return;
}
maybeExpect('=');
skipWS();
if (done()) {
parameters[name] = null;
return;
}
var value = parseParameterValue();
if (name == 'charset' && value != null) {
value = value.toLowerCase();
}
parameters[name] = value;
skipWS();
if (done()) return;
if (value[index] == valueSeparator) return;
expect(parameterSeparator);
}
}
skipWS();
_value = parseValue();
skipWS();
if (done()) return;
maybeExpect(parameterSeparator);
parseParameters();
}
}
... ...
class HttpStatus {
HttpStatus(this.code);
final int code;
static const int continue_ = 100;
static const int switchingProtocols = 101;
static const int processing = 102;
static const int ok = 200;
static const int created = 201;
static const int accepted = 202;
static const int nonAuthoritativeInformation = 203;
static const int noContent = 204;
static const int resetContent = 205;
static const int partialContent = 206;
static const int multiStatus = 207;
static const int alreadyReported = 208;
static const int imUsed = 226;
static const int multipleChoices = 300;
static const int movedPermanently = 301;
static const int found = 302;
static const int movedTemporarily = 302; // Common alias for found.
static const int seeOther = 303;
static const int notModified = 304;
static const int useProxy = 305;
static const int temporaryRedirect = 307;
static const int permanentRedirect = 308;
static const int badRequest = 400;
static const int unauthorized = 401;
static const int paymentRequired = 402;
static const int forbidden = 403;
static const int notFound = 404;
static const int methodNotAllowed = 405;
static const int notAcceptable = 406;
static const int proxyAuthenticationRequired = 407;
static const int requestTimeout = 408;
static const int conflict = 409;
static const int gone = 410;
static const int lengthRequired = 411;
static const int preconditionFailed = 412;
static const int requestEntityTooLarge = 413;
static const int requestUriTooLong = 414;
static const int unsupportedMediaType = 415;
static const int requestedRangeNotSatisfiable = 416;
static const int expectationFailed = 417;
static const int misdirectedRequest = 421;
static const int unprocessableEntity = 422;
static const int locked = 423;
static const int failedDependency = 424;
static const int upgradeRequired = 426;
static const int preconditionRequired = 428;
static const int tooManyRequests = 429;
static const int requestHeaderFieldsTooLarge = 431;
static const int connectionClosedWithoutResponse = 444;
static const int unavailableForLegalReasons = 451;
static const int clientClosedRequest = 499;
static const int internalServerError = 500;
static const int notImplemented = 501;
static const int badGateway = 502;
static const int serviceUnavailable = 503;
static const int gatewayTimeout = 504;
static const int httpVersionNotSupported = 505;
static const int variantAlsoNegotiates = 506;
static const int insufficientStorage = 507;
static const int loopDetected = 508;
static const int notExtended = 510;
static const int networkAuthenticationRequired = 511;
static const int networkConnectTimeoutError = 599;
bool get connectionError => code == null;
bool get isUnauthorized => code == unauthorized;
bool get isForbidden => code == forbidden;
bool get isNotFound => code == notFound;
bool get isServerError =>
between(internalServerError, networkConnectTimeoutError);
bool between(int begin, int end) {
return !connectionError && code >= begin && code <= end;
}
bool get isOk => between(200, 299);
bool get hasError => !isOk;
}
... ...
import 'dart:async';
import 'dart:convert';
import '../../../../get_rx/src/rx_stream/rx_stream.dart';
import '../request/request.dart';
bool isTokenChar(int byte) {
return byte > 31 && byte < 128 && !SEPARATOR_MAP[byte];
}
bool isValueChar(int byte) {
return (byte > 31 && byte < 128) ||
(byte == CharCode.SP) ||
(byte == CharCode.HT);
}
class CharCode {
static const int HT = 9;
static const int LF = 10;
static const int CR = 13;
static const int SP = 32;
static const int COMMA = 44;
static const int SLASH = 47;
static const int ZERO = 48;
static const int ONE = 49;
static const int COLON = 58;
static const int SEMI_COLON = 59;
}
const bool F = false;
const String GET_BOUNDARY = '--get-boundary-';
const bool T = true;
const SEPARATOR_MAP = [
F, F, F, F, F, F, F, F, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, T, F, T, F, F, F, F, F, T, T, F, F, T, F, F, T, //
F, F, F, F, F, F, F, F, F, F, T, T, T, T, T, T, T, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, T, T, T, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, T, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F
];
String validateField(String field) {
for (var i = 0; i < field.length; i++) {
if (!isTokenChar(field.codeUnitAt(i))) {
throw FormatException(
'Invalid HTTP header field name: ${json.encode(field)}', field, i);
}
}
return field.toLowerCase();
}
BodyBytes toBodyBytes(Stream<List<int>> stream) {
if (stream is BodyBytes) return stream;
return BodyBytes(stream);
}
final _asciiOnly = RegExp(r'^[\x00-\x7F]+$');
/// Returns whether [string] is composed entirely of ASCII-compatible
/// characters.
bool isPlainAscii(String string) => _asciiOnly.hasMatch(string);
String encodeMap(dynamic data, _Handler handler, {bool encode = true}) {
return urlEncode(data, '', encode, handler).toString();
}
StringBuffer urlEncode(
dynamic sub,
String path,
bool encode,
_Handler handler,
) {
var urlData = StringBuffer('');
var first = true;
var leftBracket = '[';
var rightBracket = ']';
if (encode) {
leftBracket = '%5B';
rightBracket = '%5D';
}
var encodeComponent = encode ? Uri.encodeQueryComponent : (e) => e;
if (sub is List) {
for (var i = 0; i < sub.length; i++) {
urlEncode(
sub[i],
// ignore: lines_longer_than_80_chars
'$path$leftBracket${(sub[i] is Map || sub[i] is List) ? i : ''}$rightBracket',
encode,
handler);
}
} else if (sub is Map) {
sub.forEach((key, value) {
if (path == '') {
urlEncode(
value,
'${encodeComponent(key as String)}',
encode,
handler,
);
} else {
urlEncode(
value,
'$path$leftBracket${encodeComponent(key as String)}$rightBracket',
encode,
handler,
);
}
});
} else {
var str = handler(path, sub);
var isNotEmpty = str != null && (str as String).trim().isNotEmpty;
if (!first && isNotEmpty) {
urlData.write('&');
}
first = false;
if (isNotEmpty) {
urlData.write(str);
}
}
return urlData;
}
Future streamToFuture(Stream stream, GetStream sink) {
var completer = Completer();
stream.listen(sink.add,
onError: sink.addError, onDone: () => completer.complete());
return completer.future;
}
void stringToBytes(String string, GetStream stream) {
stream.add(utf8.encode(string));
}
void writeLine(GetStream stream) => stream.add([13, 10]);
typedef _Handler = Function(String key, Object value);
... ...
import 'src/sockets_stub.dart'
if (dart.library.html) 'src/sockets_html.dart'
if (dart.library.io) 'src/sockets_io.dart';
class GetSocket extends BaseWebSocket {
GetSocket(
String url, {
Duration ping = const Duration(seconds: 5),
}) : super(url, ping: ping);
}
... ...
import 'dart:convert';
class Close {
final String message;
final int reason;
Close(this.message, this.reason);
@override
String toString() {
return 'Closed by server [$reason => $message]!';
}
}
typedef OpenSocket = void Function();
typedef CloseSocket = void Function(Close);
typedef MessageSocket = void Function(dynamic val);
class SocketNotifier {
var _onMessages = <MessageSocket>[];
var _onEvents = <String, MessageSocket>{};
var _onCloses = <CloseSocket>[];
var _onErrors = <CloseSocket>[];
OpenSocket open;
void addMessages(MessageSocket socket) {
_onMessages.add((socket));
}
void addEvents(String event, MessageSocket socket) {
_onEvents[event] = socket;
}
void addCloses(CloseSocket socket) {
_onCloses.add(socket);
}
void addErrors(CloseSocket socket) {
_onErrors.add((socket));
}
void notifyData(dynamic data) {
for (var item in _onMessages) {
item(data);
}
if (data is String) {
_tryOn(data);
}
}
void notifyClose(Close err) {
for (var item in _onCloses) {
item(err);
}
}
void notifyError(Close err) {
// rooms.removeWhere((key, value) => value.contains(_ws));
for (var item in _onErrors) {
item(err);
}
}
void _tryOn(String message) {
try {
var msg = jsonDecode(message);
final event = msg['type'];
final data = msg['data'];
if (_onEvents.containsKey(event)) {
_onEvents[event](data);
}
} on Exception catch (_) {
return;
}
}
void dispose() {
_onMessages = null;
_onEvents = null;
_onCloses = null;
_onErrors = null;
}
}
... ...
import 'dart:async';
import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
import '../../../get_core/get_core.dart';
import 'socket_notifier.dart';
enum ConnectionStatus {
connecting,
connected,
closed,
}
class BaseWebSocket {
String url;
WebSocket socket;
SocketNotifier socketNotifier = SocketNotifier();
Duration ping;
bool isDisposed = false;
BaseWebSocket(this.url, {this.ping = const Duration(seconds: 5)}) {
url = url.startsWith('https')
? url.replaceAll('https:', 'wss:')
: url.replaceAll('http:', 'ws:');
}
ConnectionStatus connectionStatus;
Timer _t;
void connect() {
try {
connectionStatus = ConnectionStatus.connecting;
socket = WebSocket(url);
socket.onOpen.listen((e) {
socketNotifier?.open();
_t = Timer?.periodic(ping, (t) {
socket.send('');
});
connectionStatus = ConnectionStatus.connected;
});
socket.onMessage.listen((event) {
socketNotifier.notifyData(event.data);
});
socket.onClose.listen((e) {
_t?.cancel();
connectionStatus = ConnectionStatus.closed;
socketNotifier.notifyClose(Close(e.reason, e.code));
});
socket.onError.listen((event) {
_t?.cancel();
socketNotifier.notifyError(Close(event.toString(), 0));
connectionStatus = ConnectionStatus.closed;
});
} on Exception catch (e) {
_t?.cancel();
socketNotifier.notifyError(Close(e.toString(), 500));
connectionStatus = ConnectionStatus.closed;
// close(500, e.toString());
}
}
// ignore: use_setters_to_change_properties
void onOpen(OpenSocket fn) {
socketNotifier.open = fn;
}
void onClose(CloseSocket fn) {
socketNotifier.addCloses(fn);
}
void onError(CloseSocket fn) {
socketNotifier.addErrors(fn);
}
void onMessage(MessageSocket fn) {
socketNotifier.addMessages(fn);
}
void on(String event, MessageSocket message) {
socketNotifier.addEvents(event, message);
}
void close([int status, String reason]) {
if (socket != null) socket.close(status, reason);
}
void send(dynamic data) {
if (connectionStatus == ConnectionStatus.closed) {
connect();
}
if (socket != null && socket.readyState == WebSocket.OPEN) {
socket.send(data);
} else {
Get.log('WebSocket not connected, message $data not sent');
}
}
void emit(String event, dynamic data) {
send(jsonEncode({'type': event, 'data': data}));
}
void dispose() {
socketNotifier.dispose();
socketNotifier = null;
isDisposed = true;
}
}
... ...
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import '../../../get_core/get_core.dart';
import 'socket_notifier.dart';
enum ConnectionStatus {
connecting,
connected,
closed,
}
class BaseWebSocket {
String url;
WebSocket socket;
SocketNotifier socketNotifier = SocketNotifier();
bool isDisposed = false;
BaseWebSocket(this.url, {this.ping = const Duration(seconds: 5)});
Duration ping;
bool allowSelfSigned = true;
ConnectionStatus connectionStatus;
Future connect() async {
if (isDisposed) {
socketNotifier = SocketNotifier();
}
try {
connectionStatus = ConnectionStatus.connecting;
socket = allowSelfSigned
? await _connectForSelfSignedCert(url)
: await WebSocket.connect(url);
socket.pingInterval = ping;
socketNotifier?.open();
connectionStatus = ConnectionStatus.connected;
socket.listen((data) {
socketNotifier.notifyData(data);
}, onError: (err) {
socketNotifier.notifyError(Close(err.toString(), 1005));
}, onDone: () {
connectionStatus = ConnectionStatus.closed;
socketNotifier
.notifyClose(Close('Connection Closed', socket.closeCode));
}, cancelOnError: true);
return;
} on SocketException catch (e) {
connectionStatus = ConnectionStatus.closed;
socketNotifier.notifyError(Close(e.osError.message, e.osError.errorCode));
return;
}
}
// ignore: use_setters_to_change_properties
void onOpen(OpenSocket fn) {
socketNotifier.open = fn;
}
void onClose(CloseSocket fn) {
socketNotifier.addCloses(fn);
}
void onError(CloseSocket fn) {
socketNotifier.addErrors(fn);
}
void onMessage(MessageSocket fn) {
socketNotifier.addMessages(fn);
}
void on(String event, MessageSocket message) {
socketNotifier.addEvents(event, message);
}
void close([int status, String reason]) {
if (socket != null) {
socket.close(status, reason);
}
}
void send(dynamic data) async {
if (connectionStatus == ConnectionStatus.closed) {
await connect();
}
if (socket != null) {
socket.add(data);
}
}
void dispose() {
socketNotifier.dispose();
socketNotifier = null;
isDisposed = true;
}
void emit(String event, dynamic data) {
send(jsonEncode({'type': event, 'data': data}));
}
Future<WebSocket> _connectForSelfSignedCert(String url) async {
try {
var r = Random();
var key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
var client = HttpClient(context: SecurityContext());
client.badCertificateCallback = (cert, host, port) {
Get.log(
'BaseWebSocket: Allow self-signed certificate => $host:$port. ');
return true;
};
var request = await client.getUrl(Uri.parse(url));
request.headers.add('Connection', 'Upgrade');
request.headers.add('Upgrade', 'websocket');
request.headers.add('Sec-WebSocket-Version', '13');
request.headers.add('Sec-WebSocket-Key', key.toLowerCase());
var response = await request.close();
// ignore: close_sinks
var socket = await response.detachSocket();
var webSocket = WebSocket.fromUpgradedSocket(
socket,
serverSide: false,
);
return webSocket;
} on Exception catch (_) {
rethrow;
}
}
}
... ...
class BaseWebSocket {
String url;
Duration ping;
BaseWebSocket(this.url, {this.ping = const Duration(seconds: 5)}) {
throw 'To use sockets you need dart:io or dart:html';
}
void close([int status, String reason]) {
throw 'To use sockets you need dart:io or dart:html';
}
}
... ...
... ... @@ -221,7 +221,7 @@ class GetInstance {
S _startController<S>({String tag}) {
final key = _getKey(S, tag);
final i = _singl[key].getDependency() as S;
if (i is GetLifeCycle) {
if (i is GetLifeCycleBase) {
if (i.onStart != null) {
i.onStart();
Get.log('"$key" has been initialized');
... ...
... ... @@ -115,7 +115,8 @@ class GetObserver extends NavigatorObserver {
value.removed = '';
value.previous = _extractRouteName(previousRoute) ?? '';
value.isSnackbar = newRoute.isSnackbar ? true : value.isSnackbar ?? false;
value.isBottomSheet = newRoute.isBottomSheet ? true : value.isBottomSheet ?? false;
value.isBottomSheet =
newRoute.isBottomSheet ? true : value.isBottomSheet ?? false;
value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false;
});
... ... @@ -154,7 +155,8 @@ class GetObserver extends NavigatorObserver {
value.removed = '';
value.previous = newRoute.name ?? '';
value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isBottomSheet =
currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
});
... ... @@ -184,7 +186,8 @@ class GetObserver extends NavigatorObserver {
value.removed = '';
value.previous = '$oldName';
value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isBottomSheet =
currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
});
... ... @@ -198,14 +201,15 @@ class GetObserver extends NavigatorObserver {
final currentRoute = _RouteData.ofRoute(route);
Get.log("REMOVING ROUTE $routeName");
_routeSend?.update((value) {
value.route = previousRoute;
value.isBack = false;
value.removed = routeName ?? '';
value.previous = routeName ?? '';
value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isBottomSheet =
currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
});
... ...
... ... @@ -3,7 +3,6 @@ part of rx_types;
/// global object that registers against `GetX` and `Obx`, and allows the
/// reactivity
/// of those `Widgets` and Rx values.
RxInterface getObs;
mixin RxObjectMixin<T> on NotifyManager<T> {
T _value;
... ... @@ -104,8 +103,8 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
/// Returns the current [value]
T get value {
if (getObs != null) {
getObs.addListener(subject);
if (RxInterface.proxy != null) {
RxInterface.proxy.addListener(subject);
}
return _value;
}
... ...
... ... @@ -15,6 +15,8 @@ abstract class RxInterface<T> {
/// Close the Rx Variable
void close();
static RxInterface proxy;
/// Calls [callback] with current value, when the value changes.
StreamSubscription<T> listen(void Function(T event) onData,
{Function onError, void Function() onDone, bool cancelOnError});
... ...
... ... @@ -87,8 +87,8 @@ class RxList<E> extends ListMixin<E>
@override
@protected
List<E> get value {
if (getObs != null) {
getObs.addListener(subject);
if (RxInterface.proxy != null) {
RxInterface.proxy.addListener(subject);
}
return _value;
}
... ...
... ... @@ -39,8 +39,8 @@ class RxMap<K, V> extends MapMixin<K, V>
@override
@protected
Map<K, V> get value {
if (getObs != null) {
getObs.addListener(subject);
if (RxInterface.proxy != null) {
RxInterface.proxy.addListener(subject);
}
return _value;
}
... ...
... ... @@ -61,8 +61,8 @@ class RxSet<E> extends SetMixin<E>
@override
@protected
Set<E> get value {
if (getObs != null) {
getObs.addListener(subject);
if (RxInterface.proxy != null) {
RxInterface.proxy.addListener(subject);
}
return _value;
}
... ...
... ... @@ -113,8 +113,8 @@ class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
}
Widget get notifyChildren {
final observer = getObs;
getObs = _observer;
final observer = RxInterface.proxy;
RxInterface.proxy = _observer;
final result = widget.builder(controller);
if (!_observer.canUpdate) {
throw """
... ... @@ -126,7 +126,7 @@ class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
getObs = observer;
RxInterface.proxy = observer;
return result;
}
... ...
... ... @@ -41,8 +41,8 @@ class _ObxState extends State<ObxWidget> {
}
Widget get notifyChilds {
final observer = getObs;
getObs = _observer;
final observer = RxInterface.proxy;
RxInterface.proxy = _observer;
final result = widget.build();
if (!_observer.canUpdate) {
throw """
... ... @@ -54,7 +54,7 @@ class _ObxState extends State<ObxWidget> {
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
getObs = observer;
RxInterface.proxy = observer;
return result;
}
... ...
... ... @@ -59,6 +59,12 @@ extension ContextExtensionss on BuildContext {
/// similar to [MediaQuery.of(context).padding]
ThemeData get theme => Theme.of(this);
/// Check if dark mode theme is enable
bool get isDarkMode => (theme.brightness == Brightness.dark);
/// give access to Theme.of(context).iconTheme.color
Color get iconColor => theme.iconTheme.color;
/// similar to [MediaQuery.of(context).padding]
TextTheme get textTheme => Theme.of(this).textTheme;
... ...