Flutter:如何在应用所有页面底部固定一个容器
实现跨页面固定底部AdMob广告的方案
核心思路是将广告组件放在全局父容器中,而非每个页面单独嵌入,这样页面切换时广告不会被销毁重建。以下分平台给出具体实现:
Android 实现
1. 传统View系统(XML布局)
用主Activity作为全局容器,底部固定广告View,上方用Fragment容器承载页面内容:
- 主布局
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ads="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 页面内容容器 --> <FrameLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <!-- 底部固定广告 --> <com.google.android.gms.ads.AdView android:id="@+id/adView" android:layout_width="match_parent" android:layout_height="wrap_content" ads:adSize="BANNER" ads:adUnitId="@string/ad_unit_id" /> </LinearLayout>
- 主Activity逻辑:
class MainActivity : AppCompatActivity() { private lateinit var adView: AdView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 初始化广告 adView = findViewById(R.id.adView) val adRequest = AdRequest.Builder().build() adView.loadAd(adRequest) // 加载初始页面 supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, HomeFragment()) .commit() } // 页面切换仅替换Fragment fun switchPage(fragment: Fragment) { supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit() } }
2. Jetpack Compose
利用Scaffold的bottomBar参数固定广告,通过Navigation组件切换页面:
@Composable fun MainScreen(navController: NavHostController) { Scaffold( bottomBar = { AdBanner() } ) { paddingValues -> NavHost( navController = navController, startDestination = "home", modifier = Modifier.padding(paddingValues) ) { composable("home") { HomeScreen(navController) } composable("profile") { ProfileScreen(navController) } // 其他页面路由 } } } @Composable fun AdBanner() { AndroidView( factory = { context -> AdView(context).apply { adSize = AdSize.BANNER adUnitId = context.getString(R.string.ad_unit_id) loadAd(AdRequest.Builder().build()) } }, modifier = Modifier.fillMaxWidth() ) }
iOS 实现
1. UIKit
自定义导航控制器,将广告View添加到容器底部:
class BaseNavigationController: UINavigationController { private let adView = GADBannerView(adSize: GADAdSizeBanner) override func viewDidLoad() { super.viewDidLoad() // 配置广告 adView.adUnitID = "你的广告单元ID" adView.rootViewController = self adView.load(GADRequest()) // 固定到容器底部 view.addSubview(adView) adView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ adView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), adView.leadingAnchor.constraint(equalTo: view.leadingAnchor), adView.trailingAnchor.constraint(equalTo: view.trailingAnchor) ]) // 避免内容被广告遮挡 viewControllers.first?.view.bottomAnchor.constraint(equalTo: adView.topAnchor).isActive = true } }
所有页面通过该导航控制器跳转,广告始终保留。
2. SwiftUI
用NavigationStack结合Toolbar的bottomBar固定广告:
struct MainView: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { HomeView(path: $path) .navigationDestination(for: Page.self) { page in switch page { case .profile: ProfileView(path: $path) case .settings: SettingsView(path: $path) } } .toolbar { ToolbarItem(placement: .bottomBar) { AdBannerView() } } } } } struct AdBannerView: UIViewRepresentable { func makeUIView(context: Context) -> GADBannerView { let bannerView = GADBannerView(adSize: GADAdSizeBanner) bannerView.adUnitID = "你的广告单元ID" bannerView.rootViewController = UIApplication.shared.windows.first?.rootViewController bannerView.load(GADRequest()) return bannerView } func updateUIView(_ uiView: GADBannerView, context: Context) {} } enum Page: Hashable { case profile, settings }
通用注意事项
- 测试阶段使用AdMob官方测试ID,避免违规:
- Android测试ID:
ca-app-pub-3940256099942544/6300978111 - iOS测试ID:
ca-app-pub-3940256099942544/2934735716
- Android测试ID:
- 遵守AdMob政策,不要遮挡广告或频繁触发广告请求
- 跨平台框架(Flutter/React Native)思路一致:将广告放在全局根组件底部,页面内容放在上方导航容器中
内容的提问来源于stack exchange,提问作者Mhmd Khatib
相关产品推荐
相关产品推荐

