提交 437ecd40 authored 作者: JarvanMo's avatar JarvanMo

Merge remote-tracking branch 'origin/v4'

......@@ -16,14 +16,14 @@ RUN sudo apt-get update && sudo apt-get install -y google-cloud-sdk && \
gcloud config set component_manager/disable_update_check true
RUN yes | sdkmanager \
"platforms;android-29" \
"build-tools;29.0.2" \
"platforms;android-33" \
"build-tools;33.0.2" \
"extras;google;m2repository" \
"extras;android;m2repository"
RUN yes | sdkmanager \
"platforms;android-30" \
"build-tools;28.0.3" \
"platforms;android-32" \
"build-tools;33.0.2" \
"extras;google;m2repository" \
"extras;android;m2repository"
......
......@@ -37,3 +37,4 @@ example/.pub-cache/
example/.pub/
example/build/
example
example/android/app/debug.keystore
# 4.0.0
* 重构Flutter端,现在需要`Fluwx fluwx = Fluwx();`调用fluwx实例
* 支持取消回传值的监听
* 枚举例按照dart语言规范进行了重命名
* 一些包含`WeChat`的方法删除了`WeChat`
* 部分类改为sealed class
* No_pay现已合并入Fluwx
* 将一些设置移到pubspec.yaml,具体可以参看`example/pubspec.yaml`
* 删除了log相关操作,因为现在可以通过yaml配置
* 新增一些open功能
# 4.0.0-pre.3
* `Fluwx`接口优化。合并了一些函数以优化使用体验。
* 修复Logging在iOS端不好的问题。
# 4.0.0-pre.2
* No_pay现已合并入Fluwx
* 将一些设置移到pubspec.yaml,具体可以参看`example/pubspec.yaml`
* 删除了log相关操作,因为现在可以通过yaml配置
* 增加了open()方法并删除了openWeChatApp
# 4.0.0-pre.1
* 重构Flutter端,现在需要`Fluwx fluwx = Fluwx();`调用fluwx实例
* 支持取消回传值的监听
* 枚举例按照dart语言规范进行了重命名
* 一些包含`WeChat`的方法删除了`WeChat`
* 部分类改为sealed class
* 最低dart版本>=3.1.0-26.0.dev
# 3.13.1
* 分享到小程序的thumbnail为必填
......
......@@ -30,6 +30,8 @@
## Preparation
[Migrate to V4 now](./doc/MIGRATE_TO_V4_CN.md)
`Fluwx` is good but not God. You'd better read [official documents](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1) before
integrating `Fluwx`. Then you'll understand how to generate Android signature, what's universal link for iOS, how to add URL schema for iOS and so on.
......@@ -49,23 +51,34 @@ dependencies:
`Fluwx` without pay:
```yaml
dependencies:
fluwx_no_pay: ^${latestVersion}
```
> Developers who need to exclude payment for iOS can enable `no_pay` in [pubspec.yaml](./example/pubspec.yaml).
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg)
> NOTE: Never forget to replace ^${latestVersion} with actual version.
## Configurations
`Fluwx` enables multiple configurations in the section `fluwx` of `pubspec.yaml` from v4, you can reference [pubspec.yaml](./example/pubspec.yaml)
for more details.
> For iOS, some configurations, such as url_scheme,universal_link, LSApplicationQueriesSchemes, can be configured by `fluwx`,
> what you need to do is to fill configurations in `pubspec.yaml`
- app_id. Required. It'll be used to generate scheme on iOS and register WxApi on Android side if app is cold boot.
- debug_logging. Optional. Enable logs by setting it `true`.
- flutter_activity. Optional. This is usually used by cold boot from WeChat on Android. `Fluwx` will try to launch launcher activity if not set.
- universal_link. Required for iOS. It'll be used to generate universal link on your projects.
## Register WxAPI
Register your app via `fluwx` if necessary.
```dart
registerWxApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
Fluwx fluwx = Fluwx();
fluwx.registerApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
```
The param `universalLink` only works with iOS. You can read [this document](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) to learn
how to create universalLink. You can also learn how to add URL schema, how to add `LSApplicationQueriesSchemes` in your iOS project. This is essential.
......
......@@ -29,6 +29,8 @@
## 准备
[迁移到V4指南](./doc/MIGRATE_TO_V4_CN.md)
`Fluwx` 可以做很多工作但不是所有. 在集成之前,最好读一下[官方文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1).
然后你才知道怎么生成签名,怎么使用universal link以及怎么添加URL schema等.
......@@ -46,23 +48,32 @@ dependencies:
```
![pub package](https://img.shields.io/pub/v/fluwx.svg)
`Fluwx`,不带支付:
```yaml
dependencies:
fluwx_no_pay: ^${latestVersion}
```
不带支付的`Fluwx`:
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg)
> 一些开发者并不需要在iOS端使用支付能力,此时您可以通过在[pubspec.yaml](./example/pubspec.yaml).
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg)中开启`no_pay`
> NOTE: 别忘记替换 ^${latestVersion} !!!!
## 配置
`Fluwx` 从v4开始可以在`pubspec.yaml``fluwx`进行一些配置。具体可以参考[pubspec.yaml](./example/pubspec.yaml)
> V4开始,iOS中的url_scheme,universal_link, LSApplicationQueriesSchemes可以不必开发者手动配动。只需在`pubspec.yaml`
> 中填写即可。
- app_id. 必填. 它将用于生成iOS的url_scheme以及在Android端冷启动时,重新初始化WxApi。
- debug_logging. 可选. 把它设置成`true`可以开启日志。
- flutter_activity. 可选. 这个通常是用于Android的冷启动。如果不设置任何值,`Fluwx`将尝试启动launcher activity.
- universal_link. iOS 必填. 它将用自动配置universal_link。
## 注册 WxAPI
通过 `fluwx` 注册WxApi.
```dart
registerWxApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
Fluwx fluwx = Fluwx();
fluwx.registerApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
```
参数 `universalLink` 只在iOS上有用. 查看[文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) 以便了解如何生成通用链接.
......
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
......@@ -6,3 +6,4 @@
.DS_Store
/build
/captures
.cxx
import org.yaml.snakeyaml.Yaml
group 'com.jarvan.fluwx'
version '1.0-SNAPSHOT'
......@@ -9,12 +11,13 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "org.yaml:snakeyaml:2.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
allprojects {
repositories {
google()
mavenCentral()
......@@ -23,28 +26,77 @@ rootProject.allprojects {
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 31
namespace "com.jarvan.fluwx"
compileSdk 31
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
defaultConfig {
minSdkVersion 16
consumerProguardFiles 'consumer-proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders = loadManifestPlaceholder()
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
dependencies {
api 'com.tencent.mm.opensdk:wechat-sdk-android:6.8.24'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'id.zelory:compressor:3.0.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
testImplementation 'junit:junit:4.13.2'
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen { false }
showStandardStreams = true
}
}
}
}
Map loadManifestPlaceholder() {
def path = rootProject.projectDir.parent + File.separator + "pubspec.yaml"
InputStream input = new FileInputStream(new File(path))
Yaml yaml = new Yaml()
Map projectConfig = yaml.load(input)
String appId = ""
String interruptWxRequest = "true"
String flutterActivity = ""
String debugLogging = "disabled"
Map fluwx = (Map) projectConfig.get("fluwx")
if (fluwx) {
Map android = (Map) fluwx.get("android")
if (android) {
def iwr = android.get("interrupt_wx_request")
if (iwr) {
interruptWxRequest = (String) iwr
}
def activity = android.get("flutter_activity")
if (activity) {
flutterActivity = (String) activity
}
}
def logging = fluwx.get("debug_logging")
if (logging && logging == "true") {
debugLogging = "true"
}
}
return ["WeChatAppId": appId,
"InterruptWeChatRequestByFluwx": interruptWxRequest,
"FluwxFlutterActivity": flutterActivity,
"WeChatDebugLogging": debugLogging]
}
group = "com.jarvan.fluwx"
version = "1.0-SNAPSHOT"
plugins {
id("com.android.library")
kotlin("android")
}
allprojects {
repositories {
google()
mavenCentral()
}
}
android {
namespace = "com.jarvan.fluwx"
compileSdk = 31
sourceSets {
val main by getting
main.java.srcDirs("src/main/kotlin")
val test by getting
test.java.srcDirs("src/test/kotlin")
}
defaultConfig {
minSdk = 16
consumerProguardFile("consumer-proguard-rules.txt")
}
testOptions {
unitTests.all {
it.useJUnitPlatform()
it.testLogging {
events("passed", "skipped", "failed", "standardOut", "standardError")
showStandardStreams = true
it.outputs.upToDateWhen {
false
}
}
}
}
}
dependencies {
api("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("id.zelory:compressor:3.0.1")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.mockito:mockito-core:5.0.0")
}
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jarvan.fluwx">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
......@@ -12,10 +11,26 @@
</queries>
<application>
<meta-data
android:name="WeChatAppId"
android:value="${WeChatAppId}" />
<meta-data
android:name="InterruptWeChatRequestByFluwx"
android:value="${InterruptWeChatRequestByFluwx}" />
<meta-data
android:name="WeChatDebugLogging"
android:value="${WeChatDebugLogging}" />
<meta-data
android:name="FluwxFlutterActivity"
android:value="${FluwxFlutterActivity}" />
<activity
android:name=".wxapi.FluwxWXEntryActivity"
android:launchMode="singleTask"
android:exported="false"
android:launchMode="singleTask"
android:taskAffinity="${applicationId}"
android:theme="@style/DisablePreviewTheme" />
......@@ -25,13 +40,14 @@
android:launchMode="singleTop"
android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:taskAffinity="${applicationId}"
android:theme="@style/DisablePreviewTheme"/>
android:theme="@style/DisablePreviewTheme" />
<activity-alias
android:name="${applicationId}.wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleInstance"
android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:theme="@style/DisablePreviewTheme"/>
android:theme="@style/DisablePreviewTheme" />
<provider
android:name="com.jarvan.fluwx.FluwxFileProvider"
......
......@@ -2,12 +2,25 @@ package com.jarvan.fluwx
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.annotation.NonNull
import com.jarvan.fluwx.handlers.*
import com.jarvan.fluwx.handlers.FluwxAuthHandler
import com.jarvan.fluwx.handlers.FluwxRequestHandler
import com.jarvan.fluwx.handlers.FluwxShareHandler
import com.jarvan.fluwx.handlers.FluwxShareHandlerEmbedding
import com.jarvan.fluwx.handlers.PermissionHandler
import com.jarvan.fluwx.handlers.WXAPiHandler
import com.jarvan.fluwx.utils.KEY_FLUWX_REQUEST_INFO_EXT_MSG
import com.jarvan.fluwx.utils.WXApiUtils
import com.tencent.mm.opensdk.modelbiz.*
import com.tencent.mm.opensdk.modelbiz.ChooseCardFromWXCardPackage
import com.tencent.mm.opensdk.modelbiz.OpenRankList
import com.tencent.mm.opensdk.modelbiz.OpenWebview
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessView
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview
import com.tencent.mm.opensdk.modelbiz.WXOpenCustomerServiceChat
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.SendReqCallback
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
......@@ -36,31 +49,27 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
private var context: Context? = null
private fun handelIntent(intent: Intent) {
val action = intent.action
val dataString = intent.dataString
if (Intent.ACTION_VIEW == action) {
extMsg = dataString
intent.getStringExtra(KEY_FLUWX_REQUEST_INFO_EXT_MSG)?.let {
extMsg = it
}
}
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.jarvanmo/fluwx")
channel.setMethodCallHandler(this)
val applicationContext = flutterPluginBinding.applicationContext
fluwxChannel = channel
context = flutterPluginBinding.applicationContext
authHandler = FluwxAuthHandler(channel)
shareHandler = FluwxShareHandlerEmbedding(
flutterPluginBinding.flutterAssets,
flutterPluginBinding.applicationContext
flutterPluginBinding.flutterAssets, flutterPluginBinding.applicationContext
)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
override fun onMethodCall(call: MethodCall, result: Result) {
callingChannel = fluwxChannel
when {
call.method == "registerApp" -> WXAPiHandler.registerApp(call, result, context)
call.method == "startLog" -> WXAPiHandler.startLog(call, result)
call.method == "stopLog" -> WXAPiHandler.stopLog(call, result)
call.method == "sendAuth" -> authHandler?.sendAuth(call, result)
call.method == "authByQRCode" -> authHandler?.authByQRCode(call, result)
call.method == "stopAuthByQRCode" -> authHandler?.stopAuthByQRCode(result)
......@@ -75,15 +84,18 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
call.method == "isWeChatInstalled" -> WXAPiHandler.checkWeChatInstallation(result)
call.method == "getExtMsg" -> getExtMsg(result)
call.method == "openWeChatCustomerServiceChat" -> openWeChatCustomerServiceChat(
call,
result
call, result
)
call.method == "checkSupportOpenBusinessView" -> WXAPiHandler.checkSupportOpenBusinessView(
result
)
call.method == "openBusinessView" -> openBusinessView(call, result)
call.method == "openWeChatInvoice" -> openWeChatInvoice(call, result);
call.method == "openWeChatInvoice" -> openWeChatInvoice(call, result)
call.method == "openUrl" -> openUrl(call, result)
call.method == "openRankList" -> openRankList(result)
else -> result.notImplemented()
}
}
......@@ -104,7 +116,9 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
request.timeStamp = System.currentTimeMillis().toString()
request.nonceStr = System.currentTimeMillis().toString()
request.signType = "SHA1"
request.cardSign = WXApiUtils.createSign(request.appId, request.nonceStr, request.timeStamp, request.cardType)
request.cardSign = WXApiUtils.createSign(
request.appId, request.nonceStr, request.timeStamp, request.cardType
)
val done = WXAPiHandler.wxApi?.sendReq(request)
result.success(done)
}
......@@ -122,11 +136,13 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
shareHandler?.permissionHandler = PermissionHandler(binding.activity)
handelIntent(binding.activity.intent)
FluwxRequestHandler.handleRequestInfoFromIntent(binding.activity.intent)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
// WXAPiHandler.setContext(binding.activity.applicationContext)
handelIntent(binding.activity.intent)
FluwxRequestHandler.handleRequestInfoFromIntent(binding.activity.intent)
shareHandler?.permissionHandler = PermissionHandler(binding.activity)
}
......@@ -262,8 +278,27 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
private fun openWXApp(result: Result) = result.success(WXAPiHandler.wxApi?.openWXApp())
private fun openUrl(call: MethodCall, result: Result) {
val req = OpenWebview.Req()
req.url = call.argument("url")
WXAPiHandler.wxApi?.sendReq(req, SendReqCallback {
result.success(it)
}) ?: kotlin.run {
result.success(false)
}
}
private fun openRankList(result: Result) {
val req = OpenRankList.Req()
WXAPiHandler.wxApi?.sendReq(req, SendReqCallback {
result.success(it)
}) ?: kotlin.run {
result.success(false)
}
}
override fun onNewIntent(intent: Intent): Boolean {
handelIntent(intent)
return false
}
}
......@@ -16,6 +16,7 @@
package com.jarvan.fluwx.handlers
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
......@@ -24,13 +25,15 @@ import android.os.Bundle
import android.util.Log
import androidx.core.content.ContextCompat.startActivity
import com.jarvan.fluwx.FluwxPlugin
import com.jarvan.fluwx.utils.KEY_FLUWX_REQUEST_INFO_BUNDLE
import com.jarvan.fluwx.utils.KEY_FLUWX_REQUEST_INFO_EXT_MSG
import com.jarvan.fluwx.utils.startFlutterActivity
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX
import java.security.cert.Extension
object FluwxRequestHandler {
private const val KEY_FLUWX_REQUEST_INFO_BUNDLE = "KEY_FLUWX_REQUEST_INFO_BUNDLE"
var customOnReqDelegate: ((baseReq: BaseReq, activity: Activity) -> Unit)? = null
......@@ -43,7 +46,8 @@ object FluwxRequestHandler {
}
}
private fun handleShowMessageFromWXBundle(bundle: Bundle) = handleWXShowMessageFromWX(ShowMessageFromWX.Req(bundle))
private fun handleShowMessageFromWXBundle(bundle: Bundle) =
handleWXShowMessageFromWX(ShowMessageFromWX.Req(bundle))
private fun handleRequest(req: BaseReq) {
when (req) {
......@@ -66,22 +70,21 @@ object FluwxRequestHandler {
// com.tencent.mm.opensdk.constants.ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX = 4
if (!WXAPiHandler.coolBoot) {
handleRequest(baseReq)
startSpecifiedActivity(defaultFlutterActivityAction(activity), activity = activity)
activity.startFlutterActivity()
} else {
when (baseReq) {
is ShowMessageFromWX.Req -> {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("wechatextmsg://${activity.packageName}/?extmsg=${baseReq.message.messageExt}"))
val bundle = Bundle()
baseReq.toBundle(bundle)
intent.putExtra(KEY_FLUWX_REQUEST_INFO_BUNDLE, bundle)
activity.startActivity(intent)
activity.finish()
activity.startFlutterActivity(
wxRequestBundle = Bundle().apply {
baseReq.toBundle(this)
},
bundle = Bundle().apply {
putString(
KEY_FLUWX_REQUEST_INFO_EXT_MSG,
baseReq.message.messageExt
)
})
WXAPiHandler.coolBoot = false
}catch (e:Exception) {
Log.i("fluwx","call scheme error:${e.toString()}")
}
}
}
}
......@@ -91,32 +94,18 @@ object FluwxRequestHandler {
fun onReq(baseReq: BaseReq, activity: Activity) {
try {
val packageManager = activity.packageManager
val appInfo = packageManager.getApplicationInfo(activity.packageName, PackageManager.GET_META_DATA)
val defaultHandle = appInfo.metaData.getBoolean("handleWeChatRequestByFluwx", true)
val appInfo = packageManager.getApplicationInfo(
activity.packageName,
PackageManager.GET_META_DATA
)
val defaultHandle = appInfo.metaData.getBoolean("InterruptWeChatRequestByFluwx", true)
if (defaultHandle) {
defaultOnReqDelegate(baseReq, activity)
} else {
customOnReqDelegate?.invoke(baseReq, activity)
}
} catch (e: Exception) {
Log.i("Fluwx", "can't load meta-data handleWeChatRequestByFluwx")
}
}
private fun startSpecifiedActivity(action: String, activity: Activity, bundle: Bundle? = null, bundleKey: String? = null) {
Intent(action).run {
bundleKey?.let {
putExtra(bundleKey, bundle)
}
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
activity.packageManager?.let {
resolveActivity(it)?.also {
activity.startActivity(this)
activity.finish()
Log.i("Fluwx", "can't load meta-data InterruptWeChatRequestByFluwx")
}
}
}
}
private fun defaultFlutterActivityAction(context: Context): String = "${context.packageName}.FlutterActivity"
}
\ No newline at end of file
......@@ -19,7 +19,10 @@
package com.jarvan.fluwx.handlers
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
import com.jarvan.fluwx.BuildConfig
import com.jarvan.fluwx.FluwxPlugin
import com.tencent.mm.opensdk.constants.Build
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory
......@@ -54,6 +57,16 @@ object WXAPiHandler : ILog {
fun registerApp(call: MethodCall, result: MethodChannel.Result, context: Context?) {
context?.let {
with(it) {
val appInfo =
packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val enableLogging = appInfo.metaData.getString("WeChatDebugLogging", "")
if (enableLogging == "true" && BuildConfig.DEBUG) {
startLog()
}
}
}
if (call.argument<Boolean?>("android") == false) {
return
}
......@@ -89,12 +102,15 @@ object WXAPiHandler : ILog {
wxApi == null -> {
result.error("Unassigned WxApi", "please config wxapi first", null)
}
wxApi?.isWXAppInstalled != true -> {
result.error("WeChat Not Installed", "Please install the WeChat first", null)
}
wxApi?.wxAppSupportAPI ?: 0 < Build.OPEN_BUSINESS_VIEW_SDK_INT -> {
(wxApi?.wxAppSupportAPI ?: 0) < Build.OPEN_BUSINESS_VIEW_SDK_INT -> {
result.error("WeChat Not Supported", "Please upgrade the WeChat version", null)
}
else -> {
result.success(true)
}
......@@ -102,60 +118,48 @@ object WXAPiHandler : ILog {
}
private fun registerWxAPIInternal(appId: String, context: Context) {
with(context) {
val appInfo =
packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val enableLogging = appInfo.metaData.getString("WeChatDebugLogging", "")
if (enableLogging == "true" && BuildConfig.DEBUG) {
startLog()
}
}
val api = WXAPIFactory.createWXAPI(context.applicationContext, appId)
registered = api.registerApp(appId)
wxApi = api
}
fun startLog(call: MethodCall, result: MethodChannel.Result) {
wxApi?.setLogImpl(this);
result.success(true);
}
fun stopLog(call: MethodCall, result: MethodChannel.Result) {
wxApi?.setLogImpl(null);
result.success(true);
fun startLog() {
wxApi?.setLogImpl(this)
}
override fun d(p0: String?, p1: String?) {
when {
p1 != null -> {
Log.d(p0, p1);
}
}
logToFlutter(p0,p1)
}
override fun i(p0: String?, p1: String?) {
when {
p1 != null -> {
Log.d(p0, p1);
}
}
logToFlutter(p0,p1)
}
override fun e(p0: String?, p1: String?) {
when {
p1 != null -> {
Log.d(p0, p1);
}
}
logToFlutter(p0,p1)
}
override fun v(p0: String?, p1: String?) {
when {
p1 != null -> {
Log.d(p0, p1);
}
}
logToFlutter(p0,p1)
}
override fun w(p0: String?, p1: String?) {
when {
p1 != null -> {
Log.d(p0, p1);
}
logToFlutter(p0,p1)
}
private fun logToFlutter(tag:String?,message:String?){
FluwxPlugin.callingChannel?.invokeMethod("wechatLog", mapOf(
"detail" to "$tag : $message"
))
}
}
......
package com.jarvan.fluwx.utils
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
internal const val KEY_FLUWX_REQUEST_INFO_EXT_MSG = "KEY_FLUWX_REQUEST_INFO_EXT_MSG"
internal const val KEY_FLUWX_REQUEST_INFO_BUNDLE = "KEY_FLUWX_REQUEST_INFO_BUNDLE"
internal fun Activity.startFlutterActivity(
wxRequestBundle: Bundle? = null,
bundle: Bundle? = null,
) {
flutterActivityIntent()?.also { intent ->
intent.addFluwxExtras()
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
bundle?.let {
intent.putExtras(it)
}
wxRequestBundle?.let {
intent.putExtra(KEY_FLUWX_REQUEST_INFO_BUNDLE, it)
}
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Log.w("fluwx", "Can not start activity for Intent: $intent")
} finally {
finish()
}
}
}
internal fun Context.flutterActivityIntent(): Intent? {
val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val flutterActivity = appInfo.metaData.getString("FluwxFlutterActivity", "")
return if (flutterActivity.isBlank()) {
packageManager.getLaunchIntentForPackage(packageName)
} else {
Intent().also {
it.setClassName(this, "${packageName}.$flutterActivity")
}
}
}
internal fun Intent.addFluwxExtras() {
putExtra("fluwx_payload_from_fluwx", true)
}
\ No newline at end of file
......@@ -22,6 +22,8 @@ import android.os.Bundle
import com.jarvan.fluwx.handlers.FluwxResponseHandler
import com.jarvan.fluwx.handlers.FluwxRequestHandler
import com.jarvan.fluwx.handlers.WXAPiHandler
import com.jarvan.fluwx.utils.flutterActivityIntent
import com.jarvan.fluwx.utils.startFlutterActivity
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
......@@ -38,19 +40,18 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
try {
if (!WXAPiHandler.wxApiRegistered) {
val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val wechatAppId = appInfo.metaData.getString("weChatAppId")
val wechatAppId = appInfo.metaData.getString("WeChatAppId")
if (wechatAppId != null) {
WXAPiHandler.setupWxApi(wechatAppId,this)
WXAPiHandler.coolBoot = true
} else {
Log.e("fluwx","can't load meta-data weChatAppId")
Log.w("fluwx","can't load meta-data weChatAppId")
}
}
WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) {
e.printStackTrace()
startSpecifiedActivity(defaultFlutterActivityAction())
finish()
this.startFlutterActivity()
}
}
......@@ -63,8 +64,7 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) {
e.printStackTrace()
startSpecifiedActivity(defaultFlutterActivityAction())
finish()
this.startFlutterActivity()
}
}
......@@ -80,21 +80,4 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
FluwxResponseHandler.handleResponse(resp)
finish()
}
private fun startSpecifiedActivity(action: String, bundle: Bundle? = null, bundleKey: String? = null) {
Intent(action).run {
bundleKey?.let {
putExtra(bundleKey, bundle)
}
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
packageManager?.let {
resolveActivity(packageManager)?.also {
startActivity(this)
finish()
}
}
}
}
private fun defaultFlutterActivityAction(): String = "$packageName.FlutterActivity"
}
\ No newline at end of file
package com.jarvan.fluwx
class FluwxTest {
fun test(){}
}
\ No newline at end of file
package com.jarvan.fluwx
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.test.Test
import org.mockito.Mockito
/*
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
internal class FluwxPluginTest {
@Test
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
val plugin = FluwxPlugin()
val call = MethodCall("getPlatformVersion", null)
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
plugin.onMethodCall(call, mockResult)
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
}
}
......@@ -4,8 +4,9 @@ The purpose of `sendWeChatAuth` is to get auth code and then get information for
Getting `access_token` is not supported in `fluwx`. For `access_token`, please visit [official documents](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html).
```dart
sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test");
```
Fluwx fluwx = Fluwx();
fluwx.authBy(which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_sdk_demo_test'));
```
> WHY? I think we shall fetch access_token or user info at backend.
......@@ -3,7 +3,8 @@
`sendWeChatAuth`的目的是为了获取code,拿到了code才能进行微信登录,可以通过[官方文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html)查看具体流程。
```dart
sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test");
Fluwx fluwx = Fluwx();
fluwx.authBy(which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_sdk_demo_test'));
```
> 为什么不支持获取用户信息? 我认为获取用户信息应该后端来做,即使没有后端,你也可以在dart层自己实现.
......
## Basic knowledge
### Response from WeChat
Actually, almost every result from functions like `shareToWeChat` or `payWithWeChat` which call `sendRequest` in native doesn't makes sense. The `bool` value is the result of `sendRequest`.
Actually, almost every result from functions like `share` or `pay` which call `sendRequest` in native doesn't makes sense. The `bool` value is the result of `sendRequest`.
So if you want get the real result you shall do like this:
```dart
fluwx.weChatResponseEventHandler.listen((res) {
if (res is fluwx.WeChatPaymentResponse) {
// do something here
fluwx.subscribeResponse((response) {
if (response is WeChatAuthResponse) {
}
});
```
Take a look at subclasses of `BaseWeChatResponse` for help.
Take a look at subclasses of `WeChatResponse` for help.
> NOTE: If you get `errCode = -1`, please read the WeChatSDK document for help. There are to many cases lead to that.
You can also unsubscribe response by calling `fluwx.subscribeResponse`.
### Images in WeChat
The are four built-in types of `WeChatImage` in `fluwx`:
......
......@@ -6,15 +6,17 @@
为了获取真实的回调,你应该这样做:
```dart
fluwx.weChatResponseEventHandler.listen((res) {
if (res is fluwx.WeChatPaymentResponse) {
// do something here
fluwx.subscribeResponse((response) {
if (response is WeChatAuthResponse) {
}
});
```
> 笔记: 如果你的 `errCode = -1`, 那请阅读微信官方文档,因为-1的原因数不胜数.
你也可以通过`fluwx.unsubscribeResponse`取消订阅消息。
### 图片
有四种内置 `WeChatImage`:
......
## Launch App from H5
Fluwx supports launch app from `<wx-open-launch-app>`, and pass `extInfo` to your app.
For Android side, you need add the following action for your FlutterActivity in `AndroidManifest.xml`:
```
<intent-filter>
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent-filter>
```
At the same time, you also need to add `<meta-data>` in application which used to store your WeChat AppId:
```xml
<meta-data
android:name="weChatAppId"
android:value="12345678" />
```
If you want to pass `extInfo` to Flutter, you need to add the following code in `MainActivity.kt`:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//If you didn't configure WxAPI, add the following code
WXAPiHandler.setupWxApi("wxd930ea5d5a258f4f",this)
//Get Ext-Info from Intent.
FluwxRequestHandler.handleRequestInfoFromIntent(intent)
}
```
If you want to custom your request logic, you need add the `<meta-data>` in application:
```xml
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="false" />
```
And then, set `FluwxRequestHandler.customOnReqDelegate` on your own.
## on Android 11
Please add the following queries in your app's `AndroidManifest.xml`:
```xml
<queries>
<intent>
<action android:name="${applicationId}.FlutterActivity" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent>
</queries>
```
## IOS
Please register your WXApi in your `AppDelegate`:
```oc
......
## 从H5启动app
Fluwx 支持从`<wx-open-launch-app>`启动你的app, 并且支持传递`extInfo`给你的app.
对于Android来说,你要在`AndroidManifest.xml`中给你的`Activity`加上一个标签:
```
<intent-filter>
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent-filter>
```
与此同时,你还需要在需要在application中加上`<meta-data>`,把你的appId放进去:
```xml
<meta-data
android:name="weChatAppId"
android:value="12345678" />
```
如果你想把`extInfo`传给Flutter, 你要在`MainActivity`加上如下代码:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//If you didn't configure WxAPI, add the following code
WXAPiHandler.setupWxApi("wxd930ea5d5a258f4f",this)
//Get Ext-Info from Intent.
FluwxRequestHandler.handleRequestInfoFromIntent(intent)
}
```
如果你想自定义你的调用逻辑, 你需要在application中加上`<meta-data>`:
```xml
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="false" />
```
然后, 自己实现 `FluwxRequestHandler.customOnReqDelegate`.
## 兼容Android 11
请在你的应用的`AndroidManifest.xml`中添加以下queries:
```xml
<queries>
<intent>
<action android:name="${applicationId}.FlutterActivity" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent>
</queries>
```
## IOS
请在你的`AppDelegate`中主动注册`WXApi`
......
## Upgrade to V4
`Fluwx v4` not only brings a lot exciting functionalities but also breaking changes。
- Now we need to initialize the instance using `Fluwx fluwx = Fluwx()`
- Listening response from WeChat changed to `subscribeResponse` and also adding `unsubscribeResponse` to support
cancel listening.
- Keyword `wechat` in some functions is removed.
- Some functions are extracted to a single function,and now you can pass different params instead.
- Some configurations are moved to[pubspec.yaml](../example/pubspec.yaml),for example, you can enable/disable log in `pubspec.yaml`.
- `no_pay` can be enabled by [pubspec.yaml](../example/pubspec.yaml), reference example for more details.
\ No newline at end of file
## 升级到V4
`Fluwx v4`带来了很多令人兴奋的功能,但也带来了少破坏性更新。
- 现在我们需要使用`Fluwx fluwx = Fluwx()`初始化实例
- 监听微信回调变成了`subscribeResponse`并且增加了`unsubscribeResponse`以支持取消监听
- 很多带有`wechat`关键字的函数已经把`wechat`关键字删除了
- 很多方法被整到了一个函数中,现在你可以传递不同的对象实现对应的业务
- 一些配置被移动到了[pubspec.yaml](../example/pubspec.yaml),可以通过`pubspec.yaml`配置是否开启日志等等
- `no_pay`现在也通过[pubspec.yaml](../example/pubspec.yaml)配置,具体可以参加example.
\ No newline at end of file
......@@ -3,15 +3,16 @@
Calling payment is easy but to make it work isn't not so easy:
```dart
payWithWeChat(
appId: result['appid'],
partnerId: result['partnerid'],
prepayId: result['prepayid'],
packageValue: result['package'],
nonceStr: result['noncestr'],
timeStamp: result['timestamp'],
sign: result['sign'],
);
fluwx.pay(
which: Payment(
appId: result['appid'].toString(),
partnerId: result['partnerid'].toString(),
prepayId: result['prepayid'].toString(),
packageValue: result['package'].toString(),
nonceStr: result['noncestr'].toString(),
timestamp: result['timestamp'],
sign: result['sign'].toString(),
));
```
Take a look at [payment document](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1#) for help.
......@@ -3,31 +3,16 @@
调用支付方法很简单,但想成功并不简单:
```dart
payWithWeChat(
appId: result['appid'],
partnerId: result['partnerid'],
prepayId: result['prepayid'],
packageValue: result['package'],
nonceStr: result['noncestr'],
timeStamp: result['timestamp'],
sign: result['sign'],
);
```
## iOS 支付
* 配置`URL Schemes` ,内容为应用的`AppID`, 可以登录微信开放平台查看。编辑`ios/Runner/Info.plist`
```xml
<key>CFBundleURLSchemes</key>
<array>
<string>wx84cxxxxxx</string>
</array>
```
* 配置`LSApplicationQueriesSchemes`
![image-20210523140138835](https://gitee.com/inkkk0516/typora/raw/master/image-20210523140138835.png)
fluwx.pay(
which: Payment(
appId: result['appid'].toString(),
partnerId: result['partnerid'].toString(),
prepayId: result['prepayid'].toString(),
packageValue: result['package'].toString(),
nonceStr: result['noncestr'].toString(),
timestamp: result['timestamp'],
sign: result['sign'].toString(),
));
* 使用
......
......@@ -2,18 +2,18 @@
Simple and easy:
```dart
shareToWeChat(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
fluwx.share(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
```
The destination of sharing can be SESSION(default),TIMELINE or FAVORITE.However,mini-program only support SESSION.
```dart
///[WeChatScene.SESSION]会话
///[WeChatScene.TIMELINE]朋友圈
///[WeChatScene.FAVORITE]收藏
///[WeChatScene.session]会话
///[WeChatScene.timeline]朋友圈
///[WeChatScene.favorite]收藏
enum WeChatScene {
SESSION,
TIMELINE,
FAVORITE
session,
timeline,
favorite
}
```
......
......@@ -2,21 +2,22 @@
简单:
```dart
shareToWeChat(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
fluwx.share(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
```
绝大部分分享可以分享到会话,朋友圈,收藏(小程序目前只能分享到会话)。默认分享到会话。
```dart
///[WeChatScene.SESSION]会话
///[WeChatScene.TIMELINE]朋友圈
///[WeChatScene.FAVORITE]收藏
///[WeChatScene.session]会话
///[WeChatScene.timeline]朋友圈
///[WeChatScene.favorite]收藏
enum WeChatScene {
SESSION,
TIMELINE,
FAVORITE
session,
timeline,
favorite
}
```
支持的分享各类:
- WeChatShareTextModel
......
......@@ -8,7 +8,7 @@
.buildlog/
.history
.svn/
**/Podfile.lock
migrate_working_dir/
# IntelliJ related
*.iml
......@@ -24,7 +24,6 @@
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
**/ios/Flutter/flutter_export_environment.sh
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
......@@ -33,11 +32,13 @@
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
channel: stable
project_type: app
......@@ -8,9 +8,9 @@ This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
gradle-wrapper.jar
**.classpath
**.project
**.settings
/.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
**/*.keystore
**/*.jks
......@@ -8,7 +8,6 @@ if (localPropertiesFile.exists()) {
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.")
}
......@@ -27,27 +26,26 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 31
namespace "com.jarvan.fluwx_example"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
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 "net.sourceforge.simcpux"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 16
targetSdkVersion 31
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions "branch"
buildTypes {
release {
// TODO: Add your own signing config for the release build.
......@@ -73,7 +71,4 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool 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.jarvan.fluwx_example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
......@@ -28,27 +27,15 @@
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:exported="true"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
......
......@@ -5,7 +5,6 @@ import android.os.Bundle
import com.jarvan.fluwx.handlers.FluwxRequestHandler
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
......
<?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"?>
<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
the Flutter engine 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>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- 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 -->
the Flutter engine 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">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool 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>
......@@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
......@@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
......@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
include ':app'
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
include ":app"
apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
// This is a basic Flutter integration test.
//
// Since integration tests run in a full Flutter application, they can interact
// with the host side of a plugin implementation, unlike Dart unit tests.
//
// For more information about Flutter integration tests, please see
// https://docs.flutter.dev/cookbook/testing/integration/introduction
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:fluwx/fluwx.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
});
}
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
......@@ -11,7 +12,6 @@
Icon?
**/Pods/
**/.symlinks/
Podfile.lock
profile
xcuserdata
**/.generated/
......@@ -19,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
......
......@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
......
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
......@@ -29,6 +29,9 @@ flutter_ios_podfile_setup
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
......
PODS:
- Flutter (1.0.0)
- fluwx (0.0.1):
- Flutter
- fluwx/no_pay (= 0.0.1)
- fluwx/no_pay (0.0.1):
- Flutter
- "OpenWeChatSDKNoPay (~> 2.0.2+1)"
- integration_test (0.0.1):
- Flutter
- OpenWeChatSDKNoPay (2.0.2)
DEPENDENCIES:
- Flutter (from `Flutter`)
- fluwx (from `.symlinks/plugins/fluwx/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
SPEC REPOS:
trunk:
- OpenWeChatSDKNoPay
EXTERNAL SOURCES:
Flutter:
:path: Flutter
fluwx:
:path: ".symlinks/plugins/fluwx/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
fluwx: f608fff0e3a8c30199b626fed590d83cdd820a52
integration_test: 13825b8a9334a850581300559b8839134b124670
OpenWeChatSDKNoPay: 59a9628a746352bb400329cb7f12e6dc7096bf6e
PODFILE CHECKSUM: beab77b38961de946f08660e554f80ac174dc842
COCOAPODS: 1.12.1
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
......@@ -37,6 +37,17 @@
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80F0294D02FB00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
......@@ -2,10 +2,16 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Fluwx</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string/>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
......@@ -20,22 +26,39 @@
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict/>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>wxd930ea5d5a258f4f</string>
<string>weixin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>123456</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationCategoryType</key>
<string/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
<string>weixinULAPI</string>
<string>weixinURLParamsAPI</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
......@@ -55,9 +78,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:testdomain.com</string>
</array>
</dict>
</plist>
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@import fluwx;
// This demonstrates a simple unit test of the Objective-C portion of this plugin's implementation.
//
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
@interface RunnerTests : XCTestCase
@end
@implementation RunnerTests
- (void)testExample {
FluwxPlugin *plugin = [[FluwxPlugin alloc] init];
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getPlatformVersion"
arguments:nil];
XCTestExpectation *expectation = [self expectationWithDescription:@"result block must be called"];
[plugin handleMethodCall:call
result:^(id result) {
NSString *expected = [NSString
stringWithFormat:@"iOS %@", UIDevice.currentDevice.systemVersion];
XCTAssertEqualObjects(result, expected);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:1 handler:nil];
}
@end
......@@ -26,6 +26,8 @@ class MyApp extends StatefulWidget {
}
class _MyAppState extends State<MyApp> {
Fluwx fluwx = Fluwx();
@override
void initState() {
super.initState();
......@@ -33,14 +35,14 @@ class _MyAppState extends State<MyApp> {
}
_initFluwx() async {
await registerWxApi(
await fluwx.registerApi(
appId: 'wxd930ea5d5a258f4f',
doOnAndroid: true,
doOnIOS: true,
universalLink: 'https://your.univerallink.com/link/',
);
var result = await isWeChatInstalled;
print('is installed $result');
var result = await fluwx.isWeChatInstalled;
debugPrint('is installed $result');
}
// Platform messages are asynchronous, so we initialize in an async method.
......@@ -51,20 +53,20 @@ class _MyAppState extends State<MyApp> {
return MaterialApp(
routes: <String, WidgetBuilder>{
'shareText': (context) => ShareTextPage(),
'shareImage': (context) => ShareImagePage(),
'shareWebPage': (context) => ShareWebPagePage(),
'shareMusic': (context) => ShareMusicPage(),
'shareVideo': (context) => ShareVideoPage(),
'sendAuth': (context) => SendAuthPage(),
'shareImage': (context) => const ShareImagePage(),
'shareWebPage': (context) => const ShareWebPagePage(),
'shareMusic': (context) => const ShareMusicPage(),
'shareVideo': (context) => const ShareVideoPage(),
'sendAuth': (context) => const SendAuthPage(),
'shareMiniProgram': (context) => ShareMiniProgramPage(),
'pay': (context) => PayPage(),
'launchMiniProgram': (context) => LaunchMiniProgramPage(),
'subscribeMessage': (ctx) => SubscribeMessagePage(),
'AuthByQRCode': (ctx) => AuthByQRCodePage(),
'AutoDeduct': (ctx) => SignAutoDeductPage(),
'pay': (context) => const PayPage(),
'launchMiniProgram': (context) => const LaunchMiniProgramPage(),
'subscribeMessage': (ctx) => const SubscribeMessagePage(),
'AuthByQRCode': (ctx) => const AuthByQRCodePage(),
'AutoDeduct': (ctx) => const SignAutoDeductPage(),
},
home: Scaffold(
appBar: AppBar(title: const Text('Plugin example app')),
appBar: AppBar(title: const Text('Fluwx sample')),
body: ShareSelectorPage(),
),
);
......@@ -72,6 +74,10 @@ class _MyAppState extends State<MyApp> {
}
class ShareSelectorPage extends StatelessWidget {
final Fluwx fluwx = Fluwx();
ShareSelectorPage({super.key});
@override
Widget build(BuildContext context) {
return Center(
......@@ -81,32 +87,12 @@ class ShareSelectorPage extends StatelessWidget {
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () async {
String? extMsg = await getExtMsg();
print('extMsg:$extMsg\n');
String? extMsg = await fluwx.getExtMsg();
debugPrint('extMsg:$extMsg\n');
},
child: const Text('Get ExtMessage'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () async {
bool? success = await startLog(logLevel: WXLogLevel.NORMAL);
print('startLog:$success\n');
},
child: const Text('start log'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () async {
dynamic success = await stopLog();
print('stopLog:$success\n');
},
child: const Text('stop log'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
......@@ -219,7 +205,7 @@ class ShareSelectorPage extends StatelessWidget {
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () {
openWeChatApp();
fluwx.open(target: WeChatApp());
},
child: const Text('Open WeChat App'),
),
......
......@@ -6,11 +6,11 @@ class AuthByQRCodePage extends StatefulWidget {
const AuthByQRCodePage({Key? key}) : super(key: key);
@override
_AuthByQRCodePageState createState() => _AuthByQRCodePageState();
State<AuthByQRCodePage> createState() => _AuthByQRCodePageState();
}
class _AuthByQRCodePageState extends State<AuthByQRCodePage> {
String _status = 'status';
final String _status = 'status';
Uint8List? _image;
@override
......
......@@ -5,30 +5,35 @@ class LaunchMiniProgramPage extends StatefulWidget {
const LaunchMiniProgramPage({Key? key}) : super(key: key);
@override
_LaunchMiniProgramPageState createState() => _LaunchMiniProgramPageState();
State<LaunchMiniProgramPage> createState() => _LaunchMiniProgramPageState();
}
class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> {
String? _result = '无';
final Fluwx fluwx = Fluwx();
late Function(WeChatResponse) responseListener;
@override
void dispose() {
super.dispose();
_result = null;
fluwx.unsubscribeResponse(responseListener);
}
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((res) {
if (res is WeChatLaunchMiniProgramResponse) {
responseListener = (response) {
if (response is WeChatLaunchMiniProgramResponse) {
if (mounted) {
setState(() {
_result = 'isSuccessful:${res.isSuccessful}';
_result = 'isSuccessful:${response.isSuccessful}';
});
}
}
});
}
};
@override
void dispose() {
super.dispose();
_result = null;
fluwx.subscribeResponse(responseListener);
}
@override
......@@ -41,7 +46,7 @@ class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> {
children: <Widget>[
OutlinedButton(
onPressed: () {
launchWeChatMiniProgram(username: 'gh_d43f693ca31f');
fluwx.open(target: MiniProgram(username: 'gh_d43f693ca31f'));
},
child: const Text('Launch MiniProgrom'),
),
......
......@@ -2,34 +2,38 @@ import 'dart:convert';
import 'dart:io' as H;
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx;
import 'package:fluwx/fluwx.dart';
class PayPage extends StatefulWidget {
const PayPage({Key? key}) : super(key: key);
@override
_PayPageState createState() => _PayPageState();
State<PayPage> createState() => _PayPageState();
}
class _PayPageState extends State<PayPage> {
String _url = 'https://wxpay.wxutil.com/pub_v2/app/app_pay.php';
final Fluwx fluwx = Fluwx();
final String _url = 'https://wxpay.wxutil.com/pub_v2/app/app_pay.php';
String _result = '无';
late Function(WeChatResponse) responseListener;
@override
void dispose() {
super.dispose();
fluwx.unsubscribeResponse(responseListener);
}
@override
void initState() {
super.initState();
fluwx.weChatResponseEventHandler.listen((res) {
if (res is fluwx.WeChatPaymentResponse) {
responseListener = (response) {
if (response is WeChatPaymentResponse) {
setState(() {
_result = 'pay :${res.isSuccessful}';
_result = 'pay :${response.isSuccessful}';
});
}
});
// fluwx.responseFromPayment.listen((data) {
// setState(() {
// _result = '${data.errCode}';
// });
// });
};
fluwx.subscribeResponse(responseListener);
}
@override
......@@ -46,23 +50,21 @@ class _PayPageState extends State<PayPage> {
};
var request = await h.getUrl(Uri.parse(_url));
var response = await request.close();
var data = await Utf8Decoder().bind(response).join();
var data = await const Utf8Decoder().bind(response).join();
Map<String, dynamic> result = json.decode(data);
print(result['appid']);
print(result['timestamp']);
debugPrint(result['appid']);
debugPrint(result['timestamp']);
fluwx
.payWithWeChat(
.pay(
which: Payment(
appId: result['appid'].toString(),
partnerId: result['partnerid'].toString(),
prepayId: result['prepayid'].toString(),
packageValue: result['package'].toString(),
nonceStr: result['noncestr'].toString(),
timeStamp: result['timestamp'],
timestamp: result['timestamp'],
sign: result['sign'].toString(),
)
.then((data) {
print('---》$data');
});
));
},
child: const Text('pay'),
),
......
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx;
import 'package:fluwx/fluwx.dart';
class SendAuthPage extends StatefulWidget {
const SendAuthPage({Key? key}) : super(key: key);
@override
_SendAuthPageState createState() => _SendAuthPageState();
State<SendAuthPage> createState() => _SendAuthPageState();
}
class _SendAuthPageState extends State<SendAuthPage> {
String? _result = '无';
Fluwx fluwx = Fluwx();
@override
void initState() {
super.initState();
fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) {
if (res is fluwx.WeChatAuthResponse) {
fluwx.subscribeResponse((response) {
if (response is WeChatAuthResponse) {
setState(() {
_result = 'state :${res.state} \n code:${res.code}';
_result = 'state :${response.state} \n code:${response.code}';
});
}
});
......@@ -38,10 +39,11 @@ class _SendAuthPageState extends State<SendAuthPage> {
OutlinedButton(
onPressed: () {
fluwx
.sendWeChatAuth(
.authBy(
which: NormalAuth(
scope: 'snsapi_userinfo',
state: 'wechat_sdk_demo_test',
)
))
.then((data) {});
},
child: const Text('send auth'),
......
......@@ -5,20 +5,21 @@ class ShareImagePage extends StatefulWidget {
const ShareImagePage({Key? key}) : super(key: key);
@override
_ShareImagePageState createState() => _ShareImagePageState();
State<ShareImagePage> createState() => _ShareImagePageState();
}
class _ShareImagePageState extends State<ShareImagePage> {
WeChatScene scene = WeChatScene.SESSION;
WeChatScene scene = WeChatScene.session;
String _response = '';
WeChatImage? source;
WeChatImage? thumbnail;
Fluwx fluwx = Fluwx();
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((res) {
fluwx.subscribeResponse((res) {
if (res is WeChatShareResponse) {
setState(() {
_response = 'state :${res.isSuccessful}';
......@@ -34,7 +35,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
title: const Text('shareImage'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share, color: Colors.white),
icon: const Icon(Icons.share, color: Colors.white),
onPressed: _shareImage,
),
],
......@@ -44,7 +45,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: '图片地址(仅限网络)'),
decoration: const InputDecoration(labelText: '图片地址(仅限网络)'),
controller: TextEditingController(
text: 'https://timgsa.baidu.com/timg'
'?image'
......@@ -73,7 +74,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.SESSION,
value: WeChatScene.session,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -85,7 +86,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
value: WeChatScene.timeline,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -97,7 +98,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
value: WeChatScene.favorite,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -116,7 +117,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
}
void _shareImage() {
shareToWeChat(WeChatShareImageModel(source!, thumbnail: thumbnail));
fluwx.share(WeChatShareImageModel(source!, thumbnail: thumbnail));
}
void handleRadioValueChanged(WeChatScene scene) {
......
......@@ -2,12 +2,14 @@ import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareMiniProgramPage extends StatefulWidget {
const ShareMiniProgramPage({Key? key}) : super(key: key);
@override
_ShareMiniProgramPageState createState() => _ShareMiniProgramPageState();
State<ShareMiniProgramPage> createState() => _ShareMiniProgramPageState();
}
class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
WeChatScene scene = WeChatScene.SESSION;
WeChatScene scene = WeChatScene.session;
String _webPageUrl = 'http://www.qq.com';
String _thumbnail = 'https://timgsa.baidu.com/timg'
'?image'
......@@ -22,6 +24,8 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
String _path = '/pages/media';
String _description = 'Fluwx';
Fluwx fluwx = Fluwx();
@override
Widget build(BuildContext context) {
return Scaffold(
......@@ -29,7 +33,7 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
title: const Text('ShareMiniProgram'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share, color: Colors.white),
icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share,
),
],
......@@ -95,6 +99,6 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
description: _description,
thumbnail: WeChatImage.network(_thumbnail),
);
shareToWeChat(model);
fluwx.share(model);
}
}
......@@ -5,7 +5,7 @@ class ShareMusicPage extends StatefulWidget {
const ShareMusicPage({Key? key}) : super(key: key);
@override
_ShareMusicPageState createState() => _ShareMusicPageState();
State<ShareMusicPage> createState() => _ShareMusicPageState();
}
class _ShareMusicPageState extends State<ShareMusicPage> {
......@@ -15,7 +15,8 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
String _title = 'Beyond';
String _description = 'A Popular Rock Band From China';
String _thumnail = 'images/logo.png';
WeChatScene scene = WeChatScene.SESSION;
WeChatScene scene = WeChatScene.session;
Fluwx _fluwx = Fluwx();
@override
Widget build(BuildContext context) {
......@@ -24,7 +25,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
title: const Text('ShareMusicPage'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share, color: Colors.white),
icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share,
),
],
......@@ -41,21 +42,22 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
onChanged: (str) {
_musicUrl = str;
},
decoration: InputDecoration(labelText: 'music url'),
decoration: const InputDecoration(labelText: 'music url'),
),
TextField(
controller: TextEditingController(text: 'http://www.qq.com'),
onChanged: (str) {
_musicLowBandUrl = str;
},
decoration: InputDecoration(labelText: 'music low band url'),
decoration:
const InputDecoration(labelText: 'music low band url'),
),
TextField(
controller: TextEditingController(text: 'Beyond'),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: 'title'),
decoration: const InputDecoration(labelText: 'title'),
),
TextField(
controller: TextEditingController(
......@@ -64,14 +66,14 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
onChanged: (str) {
_description = str;
},
decoration: InputDecoration(labelText: 'description'),
decoration: const InputDecoration(labelText: 'description'),
),
TextField(
controller: TextEditingController(text: 'images/logo.png'),
onChanged: (str) {
_thumnail = str;
},
decoration: InputDecoration(labelText: 'thumbnail'),
decoration: const InputDecoration(labelText: 'thumbnail'),
),
Row(
children: <Widget>[
......@@ -79,7 +81,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.SESSION,
value: WeChatScene.session,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -91,7 +93,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
value: WeChatScene.timeline,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -103,7 +105,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
value: WeChatScene.favorite,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -130,7 +132,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
thumbnail: WeChatImage.network(_thumnail),
);
shareToWeChat(model);
_fluwx.share(model);
}
void handleRadioValueChanged(WeChatScene scene) {
......
......@@ -2,13 +2,16 @@ import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareTextPage extends StatefulWidget {
const ShareTextPage({Key? key}) : super(key: key);
@override
_ShareTextPageState createState() => _ShareTextPageState();
State<ShareTextPage> createState() => _ShareTextPageState();
}
class _ShareTextPageState extends State<ShareTextPage> {
String _text = 'share text from fluwx';
WeChatScene scene = WeChatScene.SESSION;
WeChatScene scene = WeChatScene.session;
Fluwx fluwx = Fluwx();
@override
Widget build(BuildContext context) {
......@@ -31,7 +34,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
onChanged: (str) {
_text = str;
},
decoration: InputDecoration(labelText: 'TextToShare'),
decoration: const InputDecoration(labelText: 'TextToShare'),
),
Row(
children: <Widget>[
......@@ -39,7 +42,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.SESSION,
value: WeChatScene.session,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -51,7 +54,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
value: WeChatScene.timeline,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -63,7 +66,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
value: WeChatScene.favorite,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -81,9 +84,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
}
void _shareText() {
shareToWeChat(WeChatShareTextModel(_text, scene: scene)).then((data) {
print('-->$data');
});
fluwx.share(WeChatShareTextModel(_text, scene: scene));
}
void handleRadioValueChanged(WeChatScene scene) {
......
......@@ -5,16 +5,18 @@ class ShareVideoPage extends StatefulWidget {
const ShareVideoPage({Key? key}) : super(key: key);
@override
_ShareMusicPageState createState() => _ShareMusicPageState();
State<ShareVideoPage> createState() => _ShareVideoPageState();
}
class _ShareMusicPageState extends State<ShareVideoPage> {
class _ShareVideoPageState extends State<ShareVideoPage> {
String _videoUrl = 'http://www.qq.com';
String _videoLowBandUrl = 'http://www.qq.com';
String _title = 'Beyond';
String _description = 'A Popular Rock Band From China';
String _thumnail = 'images/logo.png';
WeChatScene scene = WeChatScene.SESSION;
WeChatScene scene = WeChatScene.session;
Fluwx fluwx = Fluwx();
@override
Widget build(BuildContext context) {
......@@ -23,7 +25,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
title: const Text('ShareVideoPage'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share, color: Colors.white),
icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share,
),
],
......@@ -40,21 +42,22 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
onChanged: (str) {
_videoUrl = str;
},
decoration: InputDecoration(labelText: 'video url'),
decoration: const InputDecoration(labelText: 'video url'),
),
TextField(
controller: TextEditingController(text: 'http://www.qq.com'),
onChanged: (str) {
_videoLowBandUrl = str;
},
decoration: InputDecoration(labelText: 'video low band url'),
decoration:
const InputDecoration(labelText: 'video low band url'),
),
TextField(
controller: TextEditingController(text: 'Beyond'),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: 'title'),
decoration: const InputDecoration(labelText: 'title'),
),
TextField(
controller:
......@@ -62,14 +65,14 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
onChanged: (str) {
_description = str;
},
decoration: InputDecoration(labelText: 'description'),
decoration: const InputDecoration(labelText: 'description'),
),
TextField(
controller: TextEditingController(text: 'images/logo.png'),
onChanged: (str) {
_thumnail = str;
},
decoration: InputDecoration(labelText: 'thumbnail'),
decoration: const InputDecoration(labelText: 'thumbnail'),
),
Row(
children: <Widget>[
......@@ -77,7 +80,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.SESSION,
value: WeChatScene.session,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -89,7 +92,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
value: WeChatScene.timeline,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -101,7 +104,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
value: WeChatScene.favorite,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -124,10 +127,10 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
videoLowBandUrl: _videoLowBandUrl,
thumbnail: WeChatImage.network(_thumnail),
description: _description,
scene: this.scene,
scene: scene,
title: _title,
);
shareToWeChat(model);
fluwx.share(model);
}
void handleRadioValueChanged(WeChatScene scene) {
......
......@@ -12,7 +12,9 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
String _url = 'share text from fluwx';
String _title = 'Fluwx';
String _thumnail = 'images/logo.png';
WeChatScene scene = WeChatScene.SESSION;
WeChatScene scene = WeChatScene.session;
Fluwx fluwx = Fluwx();
@override
Widget build(BuildContext context) {
......@@ -21,7 +23,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
title: const Text('ShareWebPage'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share, color: Colors.white),
icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share,
),
],
......@@ -37,21 +39,21 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
onChanged: (str) {
_url = str;
},
decoration: InputDecoration(labelText: 'web page'),
decoration: const InputDecoration(labelText: 'web page'),
),
TextField(
controller: TextEditingController(text: 'Fluwx'),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: 'thumbnail'),
decoration: const InputDecoration(labelText: 'thumbnail'),
),
TextField(
controller: TextEditingController(text: 'images/logo.png'),
onChanged: (str) {
_thumnail = str;
},
decoration: InputDecoration(labelText: 'thumbnail'),
decoration: const InputDecoration(labelText: 'thumbnail'),
),
Row(
children: <Widget>[
......@@ -59,7 +61,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.SESSION,
value: WeChatScene.session,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -71,7 +73,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
value: WeChatScene.timeline,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -83,7 +85,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
Row(
children: <Widget>[
Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
value: WeChatScene.favorite,
groupValue: scene,
onChanged: (v) {
if (v != null) handleRadioValueChanged(v);
......@@ -107,7 +109,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
thumbnail: WeChatImage.network(_thumnail),
scene: scene,
);
shareToWeChat(model);
fluwx.share(model);
}
void handleRadioValueChanged(WeChatScene scene) {
......
......@@ -6,10 +6,9 @@ class SignAutoDeductPage extends StatefulWidget {
const SignAutoDeductPage({Key? key}) : super(key: key);
@override
_SignAutoDeductPageState createState() => _SignAutoDeductPageState();
State<SignAutoDeductPage> createState() => _SignAutoDeductPageState();
}
/// see wechat [document](https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_5&index=2)
class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
TextEditingController appId =
TextEditingController(text: 'wx316f9c82e99ac105');
......@@ -25,10 +24,12 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
TextEditingController timestamp = TextEditingController(text: '');
TextEditingController returnApp = TextEditingController(text: '3');
Fluwx fluwx = Fluwx();
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((resp) {
fluwx.subscribeResponse((resp) {
print('resp = ${resp.isSuccessful}');
});
}
......@@ -54,10 +55,9 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SubscribeMessagePage'),
title: const Text('SubscribeMessagePage'),
),
body: Container(
child: ListView(
body: ListView(
children: <Widget>[
_buildTextField(
title: 'appId',
......@@ -109,7 +109,6 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
),
],
),
),
);
}
......@@ -124,7 +123,8 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
}
void _signAutoDeduct() {
autoDeDuctWeChat(
fluwx.autoDeduct(
data: AutoDeduct.detail(
appId: appId.text,
mchId: mchId.text,
planId: planId.text,
......@@ -136,6 +136,6 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
sign: sign.text,
timestamp: timestamp.text,
returnApp: '3',
);
));
}
}
......@@ -5,7 +5,7 @@ class SubscribeMessagePage extends StatefulWidget {
const SubscribeMessagePage({Key? key}) : super(key: key);
@override
_SubscribeMessagePageState createState() => _SubscribeMessagePageState();
State<SubscribeMessagePage> createState() => _SubscribeMessagePageState();
}
/// see wechat [document](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500434436_aWfqW&token=&lang=zh_CN)
......@@ -16,12 +16,13 @@ class _SubscribeMessagePageState extends State<SubscribeMessagePage> {
TextEditingController templateId = TextEditingController(
text: 'cm_vM2k3IjHcYbkGUeAfL6Fja_7Pgv4Hx_q4tA253Ss');
TextEditingController reserved = TextEditingController(text: '123');
Fluwx fluwx = Fluwx();
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((resp) {
print('resp = $resp');
fluwx.subscribeResponse((resp) {
debugPrint('resp = $resp');
});
}
......@@ -80,11 +81,12 @@ class _SubscribeMessagePageState extends State<SubscribeMessagePage> {
}
void _requestSubMsg() {
subscribeWeChatMsg(
fluwx.open(
target: SubscribeMessage(
appId: appId.text,
scene: int.tryParse(scene.text) ?? 1,
templateId: templateId.text,
reserved: reserved.text,
);
));
}
}
name: fluwx_example
description: Demonstrates how to use the fluwx plugin.
publish_to: 'none'
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ">=2.12.4 <3.0.0"
sdk: '>=3.0.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
characters: ^1.2.0
fluwx:
# When depending on this package from a real application you should use:
# fluwx: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.1+1
cupertino_icons: ^1.0.2
dev_dependencies:
integration_test:
sdk: flutter
flutter_test:
sdk: flutter
fluwx:
path: ../
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- images/logo.png
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
......@@ -63,3 +82,15 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
fluwx:
app_id: 123456
# only debug in debug mode
debug_logging: true
android:
# interrupt_wx_request: true # default is true
# flutter_activity: MainActivity #Default to launch app's launcher
ios:
universal_link: https://testdomain.com
# payment is enabled by default
# no_pay: true
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx_example/main.dart';
......@@ -12,15 +13,15 @@ import 'package:fluwx_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved.
// expect(
// find.byWidgetPredicate(
// (Widget widget) => widget is Text &&
// widget.data.startsWith('Running on:'),
// ),
// findsOneWidget,
// );
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
),
findsOneWidget,
);
});
}
<!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.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Demonstrates how to use the fluwx plugin.">
<!-- 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="fluwx_example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>fluwx_example</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
</body>
</html>
{
"name": "fluwx_example",
"short_name": "fluwx_example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "Demonstrates how to use the fluwx plugin.",
"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"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
......@@ -34,6 +34,5 @@ Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/ephemeral/
/Flutter/flutter_export_environment.sh
\ No newline at end of file
.gradle
\ No newline at end of file
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论