mp_flutter_wechat

WeChat native APIs (login, payment, and more) for Flutter apps compiled into a WeChat Mini Program by mp-flutter (flutter_miniprogram on pub.flutter-io.cn). On other platforms (Android/iOS/desktop, or a plain browser) calling these APIs throws a clear error instead of failing silently.

中文版见 README.zh.md.

Install

dependencies:
  mp_flutter_wechat: ^0.3.2

When developing inside the mp-flutter monorepo itself (e.g. the top-level example/ app), use a relative path dependency instead:

dependencies:
  mp_flutter_wechat:
    path: ../mp_flutter/packages/mp_flutter_wechat

See example/lib/main.dart in this package for a minimal, runnable usage example.

API

Method WeChat API Notes
MpWechat.isAvailable — Whether the WeChat bridge (self.__mpWechat) exists in the current environment
MpWechat.login() wx.login Returns a code; exchanging it for openid/session_key must happen on your server
MpWechat.checkSession() wx.checkSession Whether the login session is still valid; returns false on failure instead of throwing
MpWechat.requestPayment(params) wx.requestPayment params comes from your server's signed "unified order" response
MpWechat.chooseAddress() wx.chooseAddress Opens the shipping-address picker
MpWechat.scanCode({onlyFromCamera}) wx.scanCode Opens the QR/barcode scanner
MpWechat.setClipboardData(data) wx.setClipboardData Writes to the clipboard
MpWechat.getClipboardData() wx.getClipboardData Reads from the clipboard
MpWechat.getLocation({type}) wx.getLocation Default coordinate system is gcj02
MpWechat.makePhoneCall(phoneNumber) wx.makePhoneCall Places a phone call
MpWechat.setShareInfo({title, path, imageUrl, query}) — Sets the share info used by the host page's onShareAppMessage
MpWechat.menuButtonRect() wx.getMenuButtonBoundingClientRect Capsule button position, Future<Rect?> in logical pixels; null outside a Mini Program — see "Avoiding the capsule button" below
MpWechat.call(api, [params]) any wx.* Generic escape hatch for APIs not wrapped above; only supports success/fail-callback-style async APIs — sync APIs (*Sync), event subscriptions (on*/off*), and factories/handles (create*, *Manager) throw immediately instead of hanging

On non-Mini-Program platforms (the stub implementation) or on the Web when the __mpWechat bridge is absent:

  • isAvailable is false
  • checkSession() returns false (never throws)
  • setShareInfo(...) is a no-op (never throws)
  • every other method (including call) throws UnsupportedError('mp_flutter_wechat: <api> is only available in a WeChat Mini Program compiled by mp-flutter')

Server-side responsibilities (sensitive operations)

The following require an AppSecret or merchant key and must never live on the client — they belong on your business server:

  • code2Session (WeChat docs: auth.code2Session): exchange the code returned by login() for openid/unionid/session_key.
  • Unified order + signing (WeChat Pay docs: the unified-order API and the Mini Program payment signing rules): produces the timeStamp/nonceStr/ package/signType/paySign required by requestPayment().

Handling user cancellation

Failures (including the user cancelling) throw MpWechatException, whose cancelled field distinguishes "the user backed out" from other failures:

try {
  await MpWechat.requestPayment(params);
} on MpWechatException catch (e) {
  if (e.cancelled) {
    // User cancelled the payment; returning silently is fine.
  } else {
    // A real failure — e.api / e.errMsg have the details.
    showToast(e.errMsg);
  }
}

Behavior outside a Mini Program

On Android/iOS/desktop (the channel_stub.dart implementation) or in a plain browser (no self.__mpWechat on the Web, so channel_web.dart reports isAvailable == false), check MpWechat.isAvailable before deciding whether to show WeChat-specific UI, so you never hit an UnsupportedError:

if (MpWechat.isAvailable) {
  final code = await MpWechat.login();
  // ...
}

Avoiding the capsule button

Pages generated by mp-flutter render on a full-screen canvas (navigationStyle: custom); the build step injects the Mini Program's safe area (status bar/notch, bottom home indicator) into MediaQuery.padding/ viewPadding, so SafeArea, Scaffold, and AppBar already avoid those regions without changes. The capsule button in the top-right corner is not part of that padding (same as in native apps — padding only describes space the system itself occupies).

When you need to place a button or text near the top-right without it being covered by the capsule, use MpWechat.menuButtonRect() to get its rect and leave room yourself:

final rect = await MpWechat.menuButtonRect(); // null outside a Mini Program
final width = MediaQuery.sizeOf(context).width;
final rightGap = rect == null ? 16.0 : width - rect.left + 8; // +8 past the capsule's left edge
Padding(padding: EdgeInsets.only(right: rightGap), child: header);

Share info example

Each call to setShareInfo replaces the previously set share info wholesale rather than merging field by field: a call that only passes title clears any path/imageUrl/query set earlier, and the host page's onShareAppMessage only sees the fields from the latest call. If path is given and doesn't start with /, Dart throws ArgumentError immediately (Mini Program page paths must start with /).

MpWechat.setShareInfo(
  title: 'Limited-time offer',
  path: '/pages/flutter/flutter?sku=1',
  imageUrl: 'https://example.com/share.png',
);

Limitations

  • Only success/fail-callback-style wx.* APIs are supported through MpWechat.call. Synchronous APIs (*Sync), event subscriptions (on*/ off*), and factory/handle-style APIs (create*, *Manager) are out of scope and throw immediately if called through this package.
  • This package only talks to the bridge; it does not perform any sensitive, secret-bearing operation itself (see "Server-side responsibilities" above).
  • Behavior is only fully exercised inside a Mini Program compiled by mp-flutter; other platforms get the documented stub/UnsupportedError behavior, not a WeChat API polyfill.

Implementation note: exception mapping on Web

In lib/src/channel_web.dart, the JS bridge (wechat.js) rejects with an Error carrying three custom properties: mpApi/mpErrMsg/mpCancelled. Verified empirically (WeChat DevTools + dart2js output, the pay step of accept-wx.js): dart2js hands the rejected JS Error to Dart's catch unchanged, and e is JSObject holds — there's no need to switch to JSPromise.then(onFulfilled, onRejected) to receive the raw rejection value.

Even so, the catch block only maps to MpWechatException when the object actually carries mpErrMsg/mpApi (i.e. it really came from wechat.js's fail()); otherwise it rethrows unchanged. Extension-type casts are unchecked, so any JSObject can be cast to the internal bridge-error type without error — missing fields just come back null. Without this check, any rejected JS value would be treated as an MpWechatException, masking real bugs.

Libraries

mp_flutter_wechat
在 mp-flutter 编译的微信小程序中调用微信登录、支付等能力。