You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FlutterFlow支付墙按钮加载RevenueCat订阅时放大问题解决

问题描述

我在FlutterFlow中构建了一个支付墙组件,但支付按钮在加载RevenueCat订阅计划时会自行放大,iOS端按钮点击前后尺寸有明显变化。

以下是组件代码:

import '../flutter_flow/flutter_flow_theme.dart';
import '../flutter_flow/flutter_flow_util.dart';
import '../flutter_flow/flutter_flow_widgets.dart';
import '../flutter_flow/custom_functions.dart' as functions;
import '../flutter_flow/revenue_cat_util.dart' as revenue_cat;
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';

import 'paywall_model.dart';
export 'paywall_model.dart';

class PaywallWidget extends StatefulWidget {
  const PaywallWidget({Key? key}) : super(key: key);

  @override
  _PaywallWidgetState createState() => _PaywallWidgetState();
}

class _PaywallWidgetState extends State<PaywallWidget> {
  late PaywallModel _model;

  @override
  void setState(VoidCallback callback) {
    super.setState(callback);
    _model.onUpdate();
  }

  @override
  void initState() {
    super.initState();
    _model = createModel(context, () => PaywallModel());
  }

  @override
  void dispose() {
    _model.maybeDispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    context.watch<FFAppState>();

    return ClipRRect(
      borderRadius: BorderRadius.circular(0.0),
      child: Container(
        width: double.infinity,
        height: MediaQuery.of(context).size.height * 1.0,
        decoration: BoxDecoration(
          color: FlutterFlowTheme.of(context).primaryBackground,
          boxShadow: [
            BoxShadow(
              blurRadius: 4.0,
              color: Color(0x33000000),
              offset: Offset(0.0, 2.0),
            )
          ],
          borderRadius: BorderRadius.circular(0.0),
          shape: BoxShape.rectangle,
          border: Border.all(
            color: FlutterFlowTheme.of(context).primaryBackground,
          ),
        ),
        child: Padding(
          padding: EdgeInsetsDirectional.fromSTEB(12.0, 12.0, 12.0, 12.0),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Container(
                width: 500.0,
                height: 187.1,
                child: Stack(
                  children: [
                    Align(
                      alignment: AlignmentDirectional(-0.96, -0.18),
                      child: Padding(
                        padding: EdgeInsetsDirectional.fromSTEB(
                            10.0, 30.0, 10.0, 30.0),
                        child: SelectionArea(
                            child: Text(
                          revenue_cat.offerings!.current!.lifetime!.product
                              .description,
                          textAlign: TextAlign.start,
                          style: FlutterFlowTheme.of(context).bodyText2,
                        )),
                      ),
                    ),
                    Align(
                      alignment: AlignmentDirectional(-0.96, -0.85),
                      child: Padding(
                        padding: EdgeInsetsDirectional.fromSTEB(
                            10.0, 10.0, 10.0, 10.0),
                        child: SelectionArea(
                            child: AutoSizeText(
                          revenue_cat
                              .offerings!.current!.lifetime!.product.title
                              .maybeHandleOverflow(
                            maxChars: 100,
                            replacement: '…',
                          ),
                          textAlign: TextAlign.start,
                          style: FlutterFlowTheme.of(context)
                              .subtitle1
                              .override(
                                fontFamily: 'AvernirNextLTPro',
                                color: FlutterFlowTheme.of(context).primaryText,
                                useGoogleFonts: false,
                              ),
                        )),
                      ),
                    ),
                    Align(
                      alignment: AlignmentDirectional(0.0, 0.0),
                      child: Padding(
                        padding: EdgeInsetsDirectional.fromSTEB(
                            10.0, 80.0, 10.0, 0.0),
                        child: FFButtonWidget(
                          onPressed: () {
                            print('Button pressed ...');
                          },
                          text: functions.concatPriceString(revenue_cat
                              .offerings!
                              .current!
                              .lifetime!
                              .product
                              .priceString)!,
                          options: FFButtonOptions(
                            width: 300.0,
                            padding: EdgeInsetsDirectional.fromSTEB(
                                0.0, 20.0, 0.0, 20.0),
                            color: Color(0xFF485946),
                            textStyle:
                                FlutterFlowTheme.of(context).subtitle2.override(
                                      fontFamily: 'AvernirNextLTPro',
                                      color: Colors.white,
                                      useGoogleFonts: false,
                                    ),
                            elevation: 2.0,
                            borderSide: BorderSide(
                              color: Colors.transparent,
                              width: 1.0,
                            ),
                            borderRadius: BorderRadius.circular(25.0),
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
解决方案

1. 修复异步加载导致的布局重绘

RevenueCat订阅计划是异步加载的,直接访问revenue_cat.offerings!.current!.lifetime!会导致初始状态为null,加载完成后触发重建,引发按钮布局异常。

修改步骤:

  • 在PaywallModel中添加变量存储订阅产品信息
  • 在initState中异步加载订阅计划,加载完成后更新状态

示例修改:

// PaywallModel中新增变量
class PaywallModel extends FlutterFlowModel {
  // ... 原有代码
  PurchasesPackage? lifetimePackage;

  void setLifetimePackage(PurchasesPackage? package) {
    lifetimePackage = package;
    notifyListeners();
  }
}

// 组件中添加异步加载逻辑
@override
void initState() {
  super.initState();
  _model = createModel(context, () => PaywallModel());
  _loadOfferings();
}

Future<void> _loadOfferings() async {
  try {
    final offerings = await revenue_cat.getOfferings();
    if (offerings != null && offerings.current != null) {
      _model.setLifetimePackage(offerings.current!.lifetime);
    }
  } catch (e) {
    print('加载订阅计划失败: $e');
  }
}

// build方法中使用_model.lifetimePackage替代直接访问
@override
Widget build(BuildContext context) {
  context.watch<FFAppState>();
  final lifetimePackage = _model.lifetimePackage;

  // 加载中显示占位
  if (lifetimePackage == null) {
    return Center(child: SpinKitCircle(color: FlutterFlowTheme.of(context).primary));
  }

  // ... 原有布局代码,替换所有revenue_cat.offerings!.current!.lifetime!为lifetimePackage
}

2. 固定按钮尺寸,避免文本触发缩放

给按钮外层套SizedBox固定宽高,移除按钮自身的width设置,避免文本长度变化导致尺寸异常:

Align(
  alignment: AlignmentDirectional(0.0, 0.0),
  child: Padding(
    padding: EdgeInsetsDirectional.fromSTEB(10.0, 80.0, 10.0, 0.0),
    child: SizedBox(
      width: 300.0,
      height: 60.0, // 匹配按钮上下padding(20*2)+文本高度
      child: FFButtonWidget(
        onPressed: () {
          print('Button pressed ...');
        },
        text: functions.concatPriceString(lifetimePackage.product.priceString)!,
        options: FFButtonOptions(
          padding: EdgeInsetsDirectional.fromSTEB(0.0, 20.0, 0.0, 20.0),
          // 移除width设置,由外层SizedBox控制
          // width: 300.0,
          // ... 其他选项不变
        ),
      ),
    ),
  ),
)

3. 限制AutoSizeText的布局影响

给标题的AutoSizeText设置固定行数和最小字体,避免文本缩放影响父容器布局:

AutoSizeText(
  lifetimePackage.product.title.maybeHandleOverflow(
    maxChars: 100,
    replacement: '…',
  ),
  textAlign: TextAlign.start,
  maxLines: 1,
  minFontSize: 16,
  style: FlutterFlowTheme.of(context).subtitle1.override(
        fontFamily: 'AvernirNextLTPro',
        color: FlutterFlowTheme.of(context).primaryText,
        useGoogleFonts: false,
      ),
)

4. 替换Stack为Column,提升布局稳定性

Stack依赖定位容易引发布局偏移,改用Column组织标题、描述和按钮,布局逻辑更清晰稳定:

Container(
  width: 500.0,
  height: 187.1,
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // 标题
      Padding(
        padding: EdgeInsetsDirectional.fromSTEB(10.0, 10.0, 10.0, 10.0),
        child: AutoSizeText(
          lifetimePackage.product.title.maybeHandleOverflow(
            maxChars: 100,
            replacement: '…',
          ),
          textAlign: TextAlign.start,
          maxLines: 1,
          minFontSize: 16,
          style: FlutterFlowTheme.of(context).subtitle1.override(
                fontFamily: 'AvernirNextLTPro',
                color: FlutterFlowTheme.of(context).primaryText,
                useGoogleFonts: false,
              ),
        ),
      ),
      // 描述
      Padding(
        padding: EdgeInsetsDirectional.fromSTEB(10.0, 0.0, 10.0, 20.0),
        child: Text(
          lifetimePackage.product.description,
          textAlign: TextAlign.start,
          style: FlutterFlowTheme.of(context).bodyText2,
        ),
      ),
      // 按钮(居中)
      Center(
        child: SizedBox(
          width: 300.0,
          height: 60.0,
          child: FFButtonWidget(
            // ... 按钮配置
          ),
        ),
      ),
    ],
  ),
)

内容的提问来源于stack exchange,提问作者jayapriya durairajan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 10:14:58