Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b66b73a3c6 | ||
|
|
416efe6aba | ||
|
|
efa7eabfaa | ||
|
|
7d8e19d1f5 | ||
|
|
016f7911f3 | ||
|
|
49b18b5701 | ||
|
|
b4be160b93 | ||
|
|
0043428abf | ||
|
|
f1ce1c77a4 | ||
|
|
7a88585b6e | ||
|
|
4808116608 | ||
|
|
8932455fe0 | ||
|
|
ebd34f2e93 | ||
|
|
6c470d7a58 | ||
|
|
ef7136b58e | ||
|
|
e7b240655d | ||
|
|
5cbdc428e8 | ||
|
|
02d9d82575 |
46
.dockerignore
Normal file
46
.dockerignore
Normal file
@@ -0,0 +1,46 @@
|
||||
# Build outputs
|
||||
build/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
.dockerignore
|
||||
|
||||
# Docs
|
||||
*.md
|
||||
README.md
|
||||
|
||||
# Tests
|
||||
test/
|
||||
.test_coverage/
|
||||
|
||||
# Android/iOS (not needed for web)
|
||||
android/
|
||||
ios/
|
||||
macos/
|
||||
windows/
|
||||
linux/
|
||||
|
||||
# Misc
|
||||
*.log
|
||||
*.swp
|
||||
.env
|
||||
@@ -15,6 +15,10 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ANDROID_HOME: /root/android-sdk
|
||||
ANDROID_SDK_ROOT: /root/android-sdk
|
||||
GRADLE_OPTS: -Xmx2g -XX:MaxMetaspaceSize=512m
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -26,16 +30,43 @@ jobs:
|
||||
distribution: "zulu"
|
||||
java-version: "17"
|
||||
|
||||
- name: Install dependencies
|
||||
run: apt-get update && apt-get install -y jq wget unzip
|
||||
|
||||
- name: Setup Android SDK
|
||||
run: |
|
||||
mkdir -p /root/android-sdk/cmdline-tools
|
||||
cd /root/android-sdk/cmdline-tools
|
||||
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O tools.zip
|
||||
unzip -q tools.zip
|
||||
mv cmdline-tools latest
|
||||
rm tools.zip
|
||||
yes | /root/android-sdk/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null 2>&1 || true
|
||||
/root/android-sdk/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
|
||||
|
||||
- name: Setup Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: "3.27.1"
|
||||
channel: "stable"
|
||||
flutter-version: "3.36.0-0.5.pre"
|
||||
channel: "beta"
|
||||
cache: true
|
||||
|
||||
- name: Fix flutter git ownership
|
||||
run: git config --global --add safe.directory '*'
|
||||
|
||||
- name: Configure Flutter
|
||||
run: |
|
||||
flutter config --android-sdk /root/android-sdk
|
||||
flutter doctor -v
|
||||
|
||||
- name: Get dependancies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Configure Gradle memory
|
||||
run: |
|
||||
echo "org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m" >> android/gradle.properties
|
||||
echo "org.gradle.daemon=true" >> android/gradle.properties
|
||||
|
||||
- name: Build APK
|
||||
run: flutter build apk --release
|
||||
|
||||
|
||||
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
||||
# Stage 1: Build the Flutter web app
|
||||
FROM ghcr.io/cirruslabs/flutter:stable AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy pubspec files and get dependencies
|
||||
COPY pubspec.yaml pubspec.lock ./
|
||||
RUN flutter pub get
|
||||
|
||||
# Copy the rest of the app
|
||||
COPY . .
|
||||
|
||||
# Build web app
|
||||
RUN flutter build web --release
|
||||
|
||||
# Stage 2: Serve with nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy the built web app from build stage
|
||||
COPY --from=build /app/build/web /usr/share/nginx/html
|
||||
|
||||
# Copy custom nginx config
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,8 +1,13 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
android:label="quotegen_client"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<!-- AdMob App ID -->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||
android:value="ca-app-pub-5177609929140951~7846774739"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
||||
@@ -19,8 +19,8 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.9.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||
id("com.android.application") version "8.3.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
||||
|
||||
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
quotegen-web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: quotegen_web
|
||||
ports:
|
||||
- "8087:80"
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=UTC
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- nginx_proxy_manager_default
|
||||
|
||||
networks:
|
||||
nginx_proxy_manager_default:
|
||||
external: true
|
||||
@@ -34,19 +34,44 @@ PODS:
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- Google-Mobile-Ads-SDK (11.13.0):
|
||||
- GoogleUserMessagingPlatform (>= 1.1)
|
||||
- google_mobile_ads (5.3.1):
|
||||
- Flutter
|
||||
- Google-Mobile-Ads-SDK (~> 11.13.0)
|
||||
- webview_flutter_wkwebview
|
||||
- GoogleUserMessagingPlatform (3.1.0)
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- SDWebImage (5.21.1):
|
||||
- SDWebImage/Core (= 5.21.1)
|
||||
- SDWebImage/Core (5.21.1)
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- SwiftyGif (5.4.5)
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- google_mobile_ads (from `.symlinks/plugins/google_mobile_ads/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- Google-Mobile-Ads-SDK
|
||||
- GoogleUserMessagingPlatform
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
|
||||
@@ -55,14 +80,31 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
google_mobile_ads:
|
||||
:path: ".symlinks/plugins/google_mobile_ads/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
Google-Mobile-Ads-SDK: 14f57f2dc33532a24db288897e26494640810407
|
||||
google_mobile_ads: fe0e2c1764ad95323dd0e3081d0bb2d58411f957
|
||||
GoogleUserMessagingPlatform: befe603da6501006420c206222acd449bba45a9c
|
||||
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
|
||||
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4
|
||||
|
||||
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
||||
|
||||
|
||||
@@ -199,6 +199,7 @@
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
202FD1FBBA50227619E09BFC /* [CP] Embed Pods Frameworks */,
|
||||
9EB32ED1B9C2EE1090869288 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -340,6 +341,23 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
9EB32ED1B9C2EE1090869288 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F9A7E4B35A518B03A03269AF /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
||||
@@ -45,5 +45,14 @@
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>GADApplicationIdentifier</key>
|
||||
<string>ca-app-pub-5177609929140951~5220611390</string>
|
||||
<key>SKAdNetworkItems</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>SKAdNetworkIdentifier</key>
|
||||
<string>cstr6suwn9.skadnetwork</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:quotegen_client/pages/home/page.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Initialize mobile ads (AdMob) only on mobile platforms (not web or macOS)
|
||||
if (!kIsWeb && !Platform.isMacOS) {
|
||||
await MobileAds.instance.initialize();
|
||||
}
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
@@ -14,8 +23,16 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShadcnApp.router(
|
||||
title: 'Flutter Demo',
|
||||
title: 'TweetForge',
|
||||
routerConfig: _router,
|
||||
scaling: defaultTargetPlatform == TargetPlatform.android ? AdaptiveScaling.only(
|
||||
sizeScaling: 1,
|
||||
textScaling: 1.1,
|
||||
radiusScaling: 1.1
|
||||
) : null,
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorSchemes.darkBlue,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
19
lib/services/ad_service.dart
Normal file
19
lib/services/ad_service.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import "package:flutter/widgets.dart";
|
||||
|
||||
abstract class AdService {
|
||||
|
||||
/// Initialize ad SDK
|
||||
Future<void> initialize();
|
||||
|
||||
/// Load banner ad
|
||||
Future<void> loadBannerAd();
|
||||
|
||||
/// Get banner widget to display
|
||||
Widget getBannerWidget();
|
||||
|
||||
/// Dispose resources
|
||||
void dispose();
|
||||
|
||||
/// Check if ads are loaded
|
||||
bool get isLoaded;
|
||||
}
|
||||
73
lib/services/admob_service.dart
Normal file
73
lib/services/admob_service.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:google_mobile_ads/google_mobile_ads.dart";
|
||||
import "ad_service.dart";
|
||||
|
||||
class AdMobService implements AdService {
|
||||
BannerAd? _bannerAd;
|
||||
bool _isLoaded = false;
|
||||
|
||||
// Ad unit IDs
|
||||
static const String _androidBannerAdUnitId = "ca-app-pub-5177609929140951/8415596856";
|
||||
static const String _iosBannerAdUnitId = "ca-app-pub-5177609929140951/7102515188";
|
||||
|
||||
String get _bannerAdUnitId {
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
return _androidBannerAdUnitId;
|
||||
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
return _iosBannerAdUnitId;
|
||||
} else {
|
||||
throw UnsupportedError("Platform not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
await MobileAds.instance.initialize();
|
||||
print("AdMob initialized");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadBannerAd() async {
|
||||
_bannerAd = BannerAd(
|
||||
adUnitId: _bannerAdUnitId,
|
||||
size: AdSize.banner,
|
||||
request: const AdRequest(),
|
||||
listener: BannerAdListener(
|
||||
onAdLoaded: (ad) {
|
||||
print("Banner ad loaded");
|
||||
_isLoaded = true;
|
||||
},
|
||||
onAdFailedToLoad: (ad, error) {
|
||||
print("Banner ad failed to load: $error");
|
||||
ad.dispose();
|
||||
_isLoaded = false;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await _bannerAd!.load();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBannerWidget() {
|
||||
if (_bannerAd == null || !_isLoaded) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
height: 50,
|
||||
child: AdWidget(ad: _bannerAd!),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_bannerAd?.dispose();
|
||||
_bannerAd = null;
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isLoaded => _isLoaded;
|
||||
}
|
||||
3
lib/services/adsense_service.dart
Normal file
3
lib/services/adsense_service.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
// Conditional export based on platform
|
||||
export "adsense_service_stub.dart"
|
||||
if (dart.library.js_interop) "adsense_service_web.dart";
|
||||
32
lib/services/adsense_service_stub.dart
Normal file
32
lib/services/adsense_service_stub.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import "package:flutter/widgets.dart";
|
||||
import "ad_service.dart";
|
||||
|
||||
/// Stub implementation for non-web platforms
|
||||
class AdSenseService implements AdService {
|
||||
bool _isLoaded = false;
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
// Not supported on non-web platforms
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadBannerAd() async {
|
||||
// Not supported on non-web platforms
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBannerWidget() {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isLoaded => _isLoaded;
|
||||
}
|
||||
80
lib/services/adsense_service_web.dart
Normal file
80
lib/services/adsense_service_web.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import "dart:ui_web" as ui_web;
|
||||
import "dart:js" as js;
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:universal_html/html.dart" as html;
|
||||
import "ad_service.dart";
|
||||
|
||||
/// Web implementation of AdSense service
|
||||
class AdSenseService implements AdService {
|
||||
bool _isLoaded = false;
|
||||
static const String _adSlotId = "XXXXXXXXXX"; // Replace with your ad slot ID
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
// AdSense is loaded via script tag in index.html
|
||||
print("AdSense initialized (web)");
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadBannerAd() async {
|
||||
// AdSense ads are loaded automatically when widget renders
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBannerWidget() {
|
||||
final viewType = "adsense-banner-${DateTime.now().millisecondsSinceEpoch}";
|
||||
|
||||
// Register the view factory
|
||||
// ignore: undefined_prefixed_name
|
||||
ui_web.platformViewRegistry.registerViewFactory(
|
||||
viewType,
|
||||
(int viewId) => _createAdElement(),
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
height: 90,
|
||||
child: HtmlElementView(viewType: viewType),
|
||||
);
|
||||
}
|
||||
|
||||
html.Element _createAdElement() {
|
||||
// Create AdSense div
|
||||
final adContainer = html.DivElement()
|
||||
..style.width = "100%"
|
||||
..style.height = "90px"
|
||||
..style.textAlign = "center";
|
||||
|
||||
final adElement = html.Element.html("""
|
||||
<ins class="adsbygoogle"
|
||||
style="display:block"
|
||||
data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
|
||||
data-ad-slot="$_adSlotId"
|
||||
data-ad-format="auto"
|
||||
data-full-width-responsive="true"></ins>
|
||||
""");
|
||||
|
||||
adContainer.append(adElement);
|
||||
|
||||
// Push ad using dart:js instead of eval
|
||||
try {
|
||||
final adsbygoogle = js.context["adsbygoogle"];
|
||||
if (adsbygoogle != null) {
|
||||
adsbygoogle.callMethod("push", [js.JsObject.jsify({})]);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error pushing AdSense ad: $e");
|
||||
}
|
||||
|
||||
return adContainer;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isLoaded => _isLoaded;
|
||||
}
|
||||
@@ -3,6 +3,29 @@ import "dart:typed_data";
|
||||
import "package:http/http.dart" as http;
|
||||
|
||||
|
||||
class QuoteEngagement {
|
||||
final int? likes;
|
||||
final int? retweets;
|
||||
final int? replies;
|
||||
final int? views;
|
||||
|
||||
QuoteEngagement({
|
||||
this.likes,
|
||||
this.retweets,
|
||||
this.replies,
|
||||
this.views,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
if (likes != null) "likes": likes,
|
||||
if (retweets != null) "retweets": retweets,
|
||||
if (replies != null) "replies": replies,
|
||||
if (views != null) "views": views,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class QuoteRequest {
|
||||
final String? displayName;
|
||||
final String? username;
|
||||
@@ -10,6 +33,8 @@ class QuoteRequest {
|
||||
final String? text;
|
||||
final dynamic imageUrl; // can be String or Uint8List
|
||||
final int? timestamp;
|
||||
final bool? verified;
|
||||
final QuoteEngagement? engagement;
|
||||
|
||||
QuoteRequest({
|
||||
this.displayName,
|
||||
@@ -18,6 +43,8 @@ class QuoteRequest {
|
||||
this.text,
|
||||
this.imageUrl,
|
||||
this.timestamp,
|
||||
this.verified,
|
||||
this.engagement,
|
||||
});
|
||||
|
||||
|
||||
@@ -40,6 +67,8 @@ class QuoteRequest {
|
||||
"text": text,
|
||||
"imageUrl": _encodeImage(imageUrl),
|
||||
"timestamp": timestamp,
|
||||
if (verified != null) "verified": verified,
|
||||
if (engagement != null) "engagement": engagement!.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,13 +87,20 @@ class QuoteRequest {
|
||||
|
||||
if (timestamp != null) params["timestamp"] = timestamp.toString();
|
||||
|
||||
if (verified != null) params["verified"] = verified.toString();
|
||||
|
||||
// engagment is complex obj, better suited for POST reqests
|
||||
if (engagement != null) {
|
||||
params["engagement"] = jsonEncode(engagement!.toJson());
|
||||
}
|
||||
|
||||
return Uri(queryParameters: params).query;
|
||||
}
|
||||
}
|
||||
|
||||
class QuoteGeneratorApi {
|
||||
static const String _baseUrl = "https://quotes.imbenji.net";
|
||||
// static const String _baseUrl = "http://localhost:3000";
|
||||
// static const String _baseUrl = "https://quotes.imbenji.net";
|
||||
static const String _baseUrl = "http://localhost:3000";
|
||||
|
||||
|
||||
// genrate a quote image using POST
|
||||
@@ -75,7 +111,10 @@ class QuoteGeneratorApi {
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {"Content-Type": "application/json"},
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "QuoteGen-Flutter/1.0",
|
||||
},
|
||||
body: jsonEncode(requestBody),
|
||||
);
|
||||
|
||||
@@ -91,7 +130,10 @@ class QuoteGeneratorApi {
|
||||
final queryString = request.toQueryString();
|
||||
final url = Uri.parse("$_baseUrl/generate?$queryString");
|
||||
|
||||
final response = await http.get(url);
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.bodyBytes;
|
||||
@@ -110,7 +152,10 @@ class QuoteGeneratorApi {
|
||||
static Future<bool> checkHealth() async {
|
||||
try {
|
||||
final url = Uri.parse("$_baseUrl/health");
|
||||
final response = await http.get(url);
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
|
||||
328
lib/services/quote_api_v2.dart
Normal file
328
lib/services/quote_api_v2.dart
Normal file
@@ -0,0 +1,328 @@
|
||||
import "dart:convert";
|
||||
import "dart:typed_data";
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:http/http.dart" as http;
|
||||
|
||||
|
||||
|
||||
class QuoteEngagement {
|
||||
final int? likes;
|
||||
final int? retweets;
|
||||
final int? replies;
|
||||
final int? views;
|
||||
|
||||
QuoteEngagement({
|
||||
this.likes,
|
||||
this.retweets,
|
||||
this.replies,
|
||||
this.views,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
if (likes != null) "likes": likes,
|
||||
if (retweets != null) "retweets": retweets,
|
||||
if (replies != null) "replies": replies,
|
||||
if (views != null) "views": views,
|
||||
};
|
||||
}
|
||||
|
||||
factory QuoteEngagement.fromJson(Map<String, dynamic> json) {
|
||||
return QuoteEngagement(
|
||||
likes: json["likes"],
|
||||
retweets: json["retweets"],
|
||||
replies: json["replies"],
|
||||
views: json["views"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QuoteSessionRequest {
|
||||
final String? displayName;
|
||||
final String? username;
|
||||
final dynamic avatarUrl;
|
||||
final String? text;
|
||||
final dynamic imageUrl;
|
||||
final int? timestamp;
|
||||
final bool? verified;
|
||||
final QuoteEngagement? engagement;
|
||||
final bool clearAvatarUrl;
|
||||
final bool clearImageUrl;
|
||||
final bool clearEngagement;
|
||||
|
||||
QuoteSessionRequest({
|
||||
this.displayName,
|
||||
this.username,
|
||||
this.avatarUrl,
|
||||
this.text,
|
||||
this.imageUrl,
|
||||
this.timestamp,
|
||||
this.verified,
|
||||
this.engagement,
|
||||
this.clearAvatarUrl = false,
|
||||
this.clearImageUrl = false,
|
||||
this.clearEngagement = false,
|
||||
});
|
||||
|
||||
|
||||
// convert img bytes to base64 data uri
|
||||
String? _encodeImage(dynamic image) {
|
||||
if (image == null) return null;
|
||||
if (image is String) return image;
|
||||
if (image is Uint8List) {
|
||||
final base64String = base64Encode(image);
|
||||
return "data:image/png;base64,$base64String";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
|
||||
if (displayName != null) map["displayName"] = displayName;
|
||||
if (username != null) map["username"] = username;
|
||||
|
||||
if (clearAvatarUrl) {
|
||||
map["avatarUrl"] = null;
|
||||
} else {
|
||||
final encodedAvatar = _encodeImage(avatarUrl);
|
||||
if (encodedAvatar != null) map["avatarUrl"] = encodedAvatar;
|
||||
}
|
||||
|
||||
if (text != null) map["text"] = text;
|
||||
|
||||
if (clearImageUrl) {
|
||||
map["imageUrl"] = null;
|
||||
} else {
|
||||
final encodedImage = _encodeImage(imageUrl);
|
||||
if (encodedImage != null) map["imageUrl"] = encodedImage;
|
||||
}
|
||||
|
||||
if (timestamp != null) map["timestamp"] = timestamp;
|
||||
|
||||
if (verified != null) map["verified"] = verified;
|
||||
|
||||
if (clearEngagement) {
|
||||
map["engagement"] = null;
|
||||
} else if (engagement != null) {
|
||||
map["engagement"] = engagement!.toJson();
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class QuoteSession {
|
||||
final String id;
|
||||
final String? displayName;
|
||||
final String? username;
|
||||
final String? avatarUrl;
|
||||
final String? text;
|
||||
final String? imageUrl;
|
||||
final int? timestamp;
|
||||
final bool? verified;
|
||||
final QuoteEngagement? engagement;
|
||||
final int createdAt;
|
||||
final int updatedAt;
|
||||
|
||||
QuoteSession({
|
||||
required this.id,
|
||||
this.displayName,
|
||||
this.username,
|
||||
this.avatarUrl,
|
||||
this.text,
|
||||
this.imageUrl,
|
||||
this.timestamp,
|
||||
this.verified,
|
||||
this.engagement,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
factory QuoteSession.fromJson(Map<String, dynamic> json) {
|
||||
return QuoteSession(
|
||||
id: json["id"],
|
||||
displayName: json["displayName"],
|
||||
username: json["username"],
|
||||
avatarUrl: json["avatarUrl"],
|
||||
text: json["text"],
|
||||
imageUrl: json["imageUrl"],
|
||||
timestamp: json["timestamp"],
|
||||
verified: json["verified"],
|
||||
engagement: json["engagement"] != null
|
||||
? QuoteEngagement.fromJson(json["engagement"])
|
||||
: null,
|
||||
createdAt: json["createdAt"],
|
||||
updatedAt: json["updatedAt"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QuoteSnapshot {
|
||||
final String token;
|
||||
final String url;
|
||||
final String sessionId;
|
||||
final int createdAt;
|
||||
final int expiresAt;
|
||||
|
||||
QuoteSnapshot({
|
||||
required this.token,
|
||||
required this.url,
|
||||
required this.sessionId,
|
||||
required this.createdAt,
|
||||
required this.expiresAt,
|
||||
});
|
||||
|
||||
factory QuoteSnapshot.fromJson(Map<String, dynamic> json) {
|
||||
return QuoteSnapshot(
|
||||
token: json["token"],
|
||||
url: json["url"],
|
||||
sessionId: json["sessionId"],
|
||||
createdAt: json["createdAt"],
|
||||
expiresAt: json["expiresAt"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QuoteGeneratorApiV2 {
|
||||
// static const String _baseUrl = "https://quotes.imbenji.net";
|
||||
static const String _baseUrl = kDebugMode ? "http://localhost:3000" : "https://quotes.imbenji.net";
|
||||
|
||||
|
||||
// create new session
|
||||
static Future<QuoteSession> createSession(QuoteSessionRequest request) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote");
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "QuoteGen-Flutter/1.0",
|
||||
},
|
||||
body: jsonEncode(request.toJson()),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final data = jsonDecode(response.body);
|
||||
return QuoteSession.fromJson(data);
|
||||
} else {
|
||||
throw Exception("Failed to create session: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get session state
|
||||
static Future<QuoteSession> getSession(String sessionId) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote/$sessionId");
|
||||
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
return QuoteSession.fromJson(data);
|
||||
} else {
|
||||
throw Exception("Failed to get session: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
// update session fields (only send whats changed)
|
||||
static Future<QuoteSession> updateSession(
|
||||
String sessionId,
|
||||
QuoteSessionRequest updates,
|
||||
) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote/$sessionId");
|
||||
|
||||
final response = await http.patch(
|
||||
url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "QuoteGen-Flutter/1.0",
|
||||
},
|
||||
body: jsonEncode(updates.toJson()),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
return QuoteSession.fromJson(data);
|
||||
} else {
|
||||
throw Exception("Failed to update session: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
// render the session as image
|
||||
static Future<Uint8List> generateImage(String sessionId) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote/$sessionId/image");
|
||||
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.bodyBytes;
|
||||
} else {
|
||||
throw Exception("Failed to generate image: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// delete session
|
||||
static Future<void> deleteSession(String sessionId) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote/$sessionId");
|
||||
|
||||
final response = await http.delete(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode != 204) {
|
||||
throw Exception("Failed to delete session: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// create persistant snapshot link
|
||||
static Future<QuoteSnapshot> createSnapshotLink(String sessionId) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote/$sessionId/snapshot-link");
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final data = jsonDecode(response.body);
|
||||
return QuoteSnapshot.fromJson(data);
|
||||
} else {
|
||||
throw Exception("Failed to create snapshot link: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
// get snapshot image by token
|
||||
static Future<Uint8List> getSnapshot(String token) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/snapshot/$token");
|
||||
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.bodyBytes;
|
||||
} else {
|
||||
throw Exception("Failed to get snapshot: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper to get current timestamp in seconds
|
||||
static int getCurrentTimestamp() {
|
||||
return DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
}
|
||||
109
lib/widgets/ad_banner.dart
Normal file
109
lib/widgets/ad_banner.dart
Normal file
@@ -0,0 +1,109 @@
|
||||
import "dart:io" show Platform;
|
||||
import "package:flutter/foundation.dart" show kIsWeb;
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:shadcn_flutter/shadcn_flutter.dart";
|
||||
import "../services/ad_service.dart";
|
||||
import "../services/admob_service.dart";
|
||||
import "../services/adsense_service.dart";
|
||||
|
||||
class AdBanner extends StatefulWidget {
|
||||
const AdBanner({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<AdBanner> createState() => _AdBannerState();
|
||||
}
|
||||
|
||||
class _AdBannerState extends State<AdBanner> {
|
||||
late AdService _adService;
|
||||
bool _isLoading = true;
|
||||
bool _isMacOS = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// check if running on macOS
|
||||
if (!kIsWeb && Platform.isMacOS) {
|
||||
_isMacOS = true;
|
||||
_isLoading = false;
|
||||
} else {
|
||||
_initializeAds();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeAds() async {
|
||||
// Platform detection
|
||||
if (kIsWeb) {
|
||||
_adService = AdSenseService();
|
||||
} else {
|
||||
_adService = AdMobService();
|
||||
}
|
||||
|
||||
try {
|
||||
await _adService.initialize();
|
||||
await _adService.loadBannerAd();
|
||||
} catch (e) {
|
||||
print("Error initializng ads: $e");
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (!_isMacOS) {
|
||||
_adService.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// Check if we're on mobile (iOS/Android)
|
||||
bool isMobile = !kIsWeb && !_isMacOS && (Platform.isIOS || Platform.isAndroid);
|
||||
|
||||
// Show placeholder only on macOS and web when ads fail to load
|
||||
// On mobile, we want real ads or nothing
|
||||
if (_isMacOS || (!isMobile && !_isLoading && !_adService.isLoaded)) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 80,
|
||||
),
|
||||
child: OutlinedContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"ADVERTISEMENT SPACE",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.mutedForeground,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
).withPadding(
|
||||
horizontal: 8
|
||||
);
|
||||
}
|
||||
|
||||
if (_isLoading) {
|
||||
return SizedBox(
|
||||
height: 50,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _adService.getBannerWidget();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,14 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_saver/file_saver_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_saver_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin");
|
||||
file_saver_plugin_register_with_registrar(file_saver_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_saver
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -6,7 +6,17 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_picker
|
||||
import file_saver
|
||||
import path_provider_foundation
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import webview_flutter_wkwebview
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
|
||||
}
|
||||
|
||||
49
macos/Podfile.lock
Normal file
49
macos/Podfile.lock
Normal file
@@ -0,0 +1,49 @@
|
||||
PODS:
|
||||
- file_picker (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- share_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- webview_flutter_wkwebview (from `Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
file_picker:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
share_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
webview_flutter_wkwebview:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/webview_flutter_wkwebview/darwin
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af
|
||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
|
||||
share_plus: 1fa619de8392a4398bfaf176d441853922614e89
|
||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
||||
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4
|
||||
|
||||
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
@@ -27,6 +27,8 @@
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||
4B88E3BA5783FAAA20C55FF8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B1D06A2639F3E3C2D980953 /* Pods_RunnerTests.framework */; };
|
||||
66220A6CD753CFE95C5E895B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2275505DFB8F93088ED6B919 /* Pods_Runner.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -60,11 +62,15 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
027FCB389F2CAAB3130196E9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
02934033A4D23A11AC357D9C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
1B1D06A2639F3E3C2D980953 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2275505DFB8F93088ED6B919 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* quotegen_client.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "quotegen_client.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10ED2044A3C60003C045 /* quotegen_client.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = quotegen_client.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@@ -76,8 +82,12 @@
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||
5348D45CFF6462B1764A19C6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
BCF862636D90C513DDDEC9AC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
BF503F87C67D76C2AAECB687 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
F88395ACF672EC99D7216FE7 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -85,6 +95,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4B88E3BA5783FAAA20C55FF8 /* Pods_RunnerTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -92,12 +103,27 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
66220A6CD753CFE95C5E895B /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1B8C4A4125941B1E08A389DC /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
02934033A4D23A11AC357D9C /* Pods-Runner.debug.xcconfig */,
|
||||
5348D45CFF6462B1764A19C6 /* Pods-Runner.release.xcconfig */,
|
||||
BF503F87C67D76C2AAECB687 /* Pods-Runner.profile.xcconfig */,
|
||||
027FCB389F2CAAB3130196E9 /* Pods-RunnerTests.debug.xcconfig */,
|
||||
BCF862636D90C513DDDEC9AC /* Pods-RunnerTests.release.xcconfig */,
|
||||
F88395ACF672EC99D7216FE7 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -125,6 +151,7 @@
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||
33CC10EE2044A3C60003C045 /* Products */,
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||
1B8C4A4125941B1E08A389DC /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -175,6 +202,8 @@
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2275505DFB8F93088ED6B919 /* Pods_Runner.framework */,
|
||||
1B1D06A2639F3E3C2D980953 /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -186,6 +215,7 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
2910AB2B6910A71DCB69BE84 /* [CP] Check Pods Manifest.lock */,
|
||||
331C80D1294CF70F00263BE5 /* Sources */,
|
||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||
331C80D3294CF70F00263BE5 /* Resources */,
|
||||
@@ -204,11 +234,13 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
0E297FF21CD9AA21EB56B11C /* [CP] Check Pods Manifest.lock */,
|
||||
33CC10E92044A3C60003C045 /* Sources */,
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||
33CC10EB2044A3C60003C045 /* Resources */,
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||
4051E39AED5CF4F08B394FE0 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -291,6 +323,50 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0E297FF21CD9AA21EB56B11C /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2910AB2B6910A71DCB69BE84 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
@@ -329,6 +405,23 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||
};
|
||||
4051E39AED5CF4F08B394FE0 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -380,6 +473,7 @@
|
||||
/* Begin XCBuildConfiguration section */
|
||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 027FCB389F2CAAB3130196E9 /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -394,6 +488,7 @@
|
||||
};
|
||||
331C80DC294CF71000263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = BCF862636D90C513DDDEC9AC /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -408,6 +503,7 @@
|
||||
};
|
||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = F88395ACF672EC99D7216FE7 /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
@@ -8,5 +8,9 @@
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,5 +4,9 @@
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
30
nginx.conf
Normal file
30
nginx.conf
Normal file
@@ -0,0 +1,30 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Main Flutter app
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
}
|
||||
312
pubspec.lock
312
pubspec.lock
@@ -41,6 +41,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -89,6 +97,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -105,6 +121,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.2"
|
||||
dio:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio
|
||||
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
email_validator:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -137,6 +169,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -145,6 +185,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.3.7"
|
||||
file_saver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_saver
|
||||
sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.14"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -192,6 +248,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.0.1"
|
||||
google_mobile_ads:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_mobile_ads
|
||||
sha256: "0d4a3744b5e8ed1b8be6a1b452d309f811688855a497c6113fc4400f922db603"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.6"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -216,6 +288,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.7.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
jovial_misc:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -296,6 +376,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -304,6 +392,54 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -320,6 +456,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -360,6 +504,78 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.47"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: share_plus
|
||||
sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.1.4"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_platform_interface
|
||||
sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
skeletonizer:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -429,6 +645,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
universal_html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: universal_html
|
||||
sha256: c0bcae5c733c60f26c7dfc88b10b0fd27cbcc45cb7492311cdaa6067e21c9cd4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_io
|
||||
sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -453,6 +725,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
webview_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.13.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.11"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.14.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: e49f378ed066efb13fc36186bbe0bd2425630d4ea0dbc71a18fdd0e4d8ed8ebc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.23.5"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -461,6 +765,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -35,6 +35,15 @@ dependencies:
|
||||
http: ^1.2.2
|
||||
file_picker: ^8.1.6
|
||||
image: ^4.3.0
|
||||
share_plus: ^10.1.4
|
||||
path_provider: ^2.1.5
|
||||
shared_preferences: ^2.3.4
|
||||
intl: ^0.19.0
|
||||
file_saver: ^0.2.14
|
||||
|
||||
# Ad integration
|
||||
google_mobile_ads: ^5.2.0
|
||||
universal_html: ^2.2.4
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
|
||||
<title>quotegen_client</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<!-- Google AdSense -->
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXXXXX"
|
||||
crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_saver/file_saver_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FileSaverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSaverPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_saver
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
Reference in New Issue
Block a user