docs: 完善 fluwx 5.x 迁移与 AdminLayout 跳转备注

- main.dart: 说明 fluwx 5.x 实例共享机制与迁移前后对比
- welcome_page.dart: 说明 addSubscriber/NormalAuth/WeChatAuthResponse 用法,
  以及 fluwx 3.x → 5.x API 对照
- AdminLayout.tsx: 说明 useEffect 跳转的原因(React #310 错误根因)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-03-04 09:00:18 -08:00
parent 2e66db08ef
commit a27baa1181
3 changed files with 35 additions and 6 deletions

View File

@ -100,7 +100,14 @@ export const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children
const { isAuthenticated, isLoading, user, logout } = useAuth();
const [collapsed, setCollapsed] = useState(false);
// 未登录 → /login必须在 useEffect 中跳转,不能在 render 期间调用 router
// 未登录 → 跳转 /login
//
// ⚠️ 必须在 useEffect 中执行,不能直接在 render 函数体内调用 router.replace()。
// 原因render 期间调用 router 会触发父组件的状态更新React #310 错误):
// "Cannot update a component while rendering a different component"
//
// useEffect 在 commit 阶段DOM 更新后)执行,此时可以安全地触发导航。
// 依赖数组包含 router 是为了满足 exhaustive-deps lint 规则router 引用稳定不会重复触发。
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.replace('/login');

View File

@ -50,13 +50,19 @@ class _WelcomePageState extends State<WelcomePage> {
// 'profile': URL
final _googleSignIn = GoogleSignIn(scopes: ['email', 'profile']);
// fluwx 5.x 使registerApi main.dart
// fluwx 5.x API
// (3.x): Stream `weChatResponseEventHandler.distinct().listen(...)`
// (5.x): `Fluwx().addSubscriber((response) {...})`
//
// Fluwx() MethodChannel SDK
// registerApi main.dart app 使
final _fluwx = Fluwx();
@override
void initState() {
super.initState();
// App code
// // subscriber
// `is WeChatAuthResponse`
_fluwx.addSubscriber((response) {
if (response is WeChatAuthResponse && mounted) {
_handleWechatAuthResp(response);
@ -77,10 +83,20 @@ class _WelcomePageState extends State<WelcomePage> {
return;
}
setState(() => _wechatLoading = true);
// NormalAuth: OAuth App
// scope: 'snsapi_userinfo'
// 'snsapi_base' openid
// state: CSRF
// : sendWeChatAuth(scope: ..., state: ...)
// : authBy(which: NormalAuth(scope: ..., state: ...))
await _fluwx.authBy(which: NormalAuth(scope: 'snsapi_userinfo', state: 'genex_login'));
// addSubscriber
// addSubscriber
}
// WeChatAuthResponse :
// errCode: 0 = , -4 = , -2 =
// code: 5 access_token + unionid
// : WXAuthResp: WeChatAuthResponse
Future<void> _handleWechatAuthResp(WeChatAuthResponse resp) async {
setState(() => _wechatLoading = false);
if ((resp.errCode ?? -1) != 0 || resp.code == null) return;

View File

@ -43,17 +43,23 @@ import 'features/profile/presentation/pages/share_page.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// SDK
// SDK (fluwx 5.x)
// WECHAT_APP_ID --dart-define
// flutter build apk --dart-define=WECHAT_APP_ID=wx0000000000000000
// flutter build ipa --dart-define=WECHAT_APP_ID=wx0000000000000000
//
// WECHAT_APP_ID / CI
// WelcomePage AppisWeChatInstalled=false
// WelcomePage isWeChatInstalled=false
//
// universalLink: iOS Universal Links
// apple-app-site-association : https://www.gogenex.com/wechat/apple-app-site-association
// : https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Universal_Links/Universal_Links.html
//
// fluwx 5.x
// (3.x): registerWxApi(appId: ..., universalLink: ...)
// (5.x): Fluwx().registerApi(appId: ..., universalLink: ...)
// app Fluwx MethodChannel
// WelcomePage Fluwx()
const wechatAppId = String.fromEnvironment('WECHAT_APP_ID', defaultValue: '');
if (wechatAppId.isNotEmpty) {
await Fluwx().registerApi(appId: wechatAppId, universalLink: 'https://www.gogenex.com/wechat/');