Posts tagged with flutter

I have a Flutter newsreader app that uses a WordPress JSON feed for its content. Im using Advanced Ads Wordpress plugin and its injecting the same ads that are on the web into the JSON Feed with the post content.

When my WebView loads it automatically spawns an external browser. This is without user interaction. The ads are loading fine in the webview. I had the same issue with youtube but applying the same solution to the google ads URL does not work.

import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; import 'data.dart'; // Post model import 'html.dart'; // For building HTML content import 'package:share_plus/share_plus.dart'; // Import share_plus class PostDetailScreen extends StatefulWidget{   final Post post;   final List<Post> recentPosts;   const PostDetailScreen({Key? key, required this.post, required this.recentPosts}) : super(key: key);   @override   PostDetailScreenState createState() => PostDetailScreenState(); } class PostDetailScreenState extends State<PostDetailScreen>{   late WebViewController _controller;   String htmlContent = '';   bool isLoading = true;   @override   void initState() {     super.initState();     loadContent();   }   Future<void> loadContent() async {     String encodedVisbyFont = await loadEncodedFont();     String encodedCanelaFont = await loadCanelaFont();     htmlContent = buildHtmlContent(         widget.post.imageUrl,         widget.post.title,         widget.post.content,         widget.post.author,         widget.post.modified,         encodedVisbyFont,         encodedCanelaFont,         widget.recentPosts.take(10).toList()     );     setState(() {       isLoading = false;     });   }   Future<void> updatePostContent(String postLink) async {     final selectedPost = widget.recentPosts.firstWhere(           (post) => post.link == postLink,       orElse: () => widget.post,     );     String updatedContent = buildHtmlContent(       selectedPost.imageUrl,       selectedPost.title,       selectedPost.content,       selectedPost.author,       selectedPost.modified,       await loadEncodedFont(),       await loadCanelaFont(),       widget.recentPosts.take(10).toList(),     );     setState(() {       htmlContent = updatedContent;     });     _controller.loadUrl(Uri.dataFromString(       htmlContent,       mimeType: 'text/html',       encoding: Encoding.getByName('utf-8'),     ).toString());   }   @override   Widget build(BuildContext context) {     final bool tablet = MediaQuery.of(context).size.width > 600;     final double titleFontSize = tablet ? 36.0 : 24.0;     final double iconSize = tablet ? 36.0 : 30.0;     final EdgeInsetsGeometry iconPadding = tablet ? const EdgeInsets.all(12.0) : const EdgeInsets.all(8.0);     final double appBarHeight = tablet ? 70.0 : 56.0;     return Scaffold(       appBar: PreferredSize(         preferredSize: Size.fromHeight(appBarHeight),         child: AppBar(           title: Text(             'FRENCHLY',             style: TextStyle(               fontFamily: 'Oswald',               fontSize: titleFontSize,               fontWeight: FontWeight.bold,               color: Colors.white,             ),           ),           backgroundColor: const Color(0xFF1D5986),           centerTitle: true,           iconTheme: IconThemeData(color: Colors.white, size: iconSize),           actions: <Widget>[             Padding(               padding: iconPadding,               child: IconButton(                 icon: Icon(Icons.share, color: Colors.white, size: iconSize),                 onPressed: () {                   Share.share('${widget.post.title}\n\n${widget.post.link}');                 },               ),             ),           ],         ),       ),       body: isLoading           ? const Center(child: CircularProgressIndicator())           : WebView(         initialUrl: Uri.dataFromString(           htmlContent,           mimeType: 'text/html',           encoding: Encoding.getByName('utf-8'),         ).toString(),         javascriptMode: JavascriptMode.unrestricted,         onWebViewCreated: (WebViewController controller) {           _controller = controller;         },         navigationDelegate: (NavigationRequest request) async {           if (request.url.startsWith('post://')) {             final postLink = Uri.decodeFull(request.url.substring(7));             updatePostContent(postLink);             return NavigationDecision.prevent;           } else if (request.url.contains('youtube.com') || request.url.contains('youtu.be')) {             // Load YouTube URLs in the WebView itself             return NavigationDecision.navigate;           } else if (await canLaunchUrl(Uri.parse(request.url))) {             await launchUrl(Uri.parse(request.url), mode: LaunchMode.externalApplication);             return NavigationDecision.prevent;           }           return NavigationDecision.navigate;         },       ),     );   } } 

This is my Ad provider

import 'package:bg_remover/services/ads_helper.dart'; import 'package:flutter/material.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; class AdaptiveBannerAdProvider with ChangeNotifier{   BannerAd? _anchoredAdaptiveAd;   bool _isLoaded = false;   BannerAd? get anchoredAdaptiveAd => _anchoredAdaptiveAd;   bool get isAdLoaded => _isLoaded;   Future<void> loadAnchoredAdaptiveAd(BuildContext context) async {     final AnchoredAdaptiveBannerAdSize? size =         await AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(             MediaQuery.of(context).size.width.truncate());     if (size == null) {       debugPrint('Unable to get height of anchored banner.');       return;     }     _anchoredAdaptiveAd = BannerAd(       adUnitId: AdHelper.bannerAdUnitId,       size: size,       request: const AdRequest(),       listener: BannerAdListener(         onAdLoaded: (Ad ad) {           debugPrint('$ad has been >>>>>loaded: ${ad.responseInfo}');           //there was an error of @This AdWidget is already in the Widget tree@           // so for that we apply this widgetsBinding to notify listener           //and place the banner add in FutureBuilder and that's work           // WidgetsBinding.instance.addPostFrameCallback((_) {           //   _isLoaded = true;           //   notifyListeners();           // });           // Ad was not showing when loaded           _isLoaded = true;           notifyListeners();         },         onAdFailedToLoad: (Ad ad, LoadAdError error) {           debugPrint('Anchored adaptive banner failedToLoad: $error');           ad.dispose();         },       ),     );     return _anchoredAdaptiveAd!.load();   }   void disposeAd() {     _anchoredAdaptiveAd?.dispose();   } } 

call it here to display Ad:

 import 'package:bg_remover/utils/routes/routes_name.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:gap/gap.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:provider/provider.dart'; import '../provider/ads_provider/adaptiveBanner_ad_provider.dart'; import '../utils/colors.dart'; import '../utils/images_assets_path.dart'; import '../utils/strings.dart'; import 'bottom_tabs/home_page.dart'; import 'bottom_tabs/project_page.dart'; import 'bottom_tabs/settings_page.dart'; class HomeFeedPage extends StatefulWidget{   const HomeFeedPage({Key? key}) : super(key: key);   @override   State<HomeFeedPage> createState() => _HomeFeedPageState(); } class _HomeFeedPageState extends State<HomeFeedPage>{   int index = 1;   final List<Widget> pages = const [     ProjectPage(),     HomePage(),     SettingsPage(),   ];   final List<String> icons = [     AssetImages.projectIcon,     AssetImages.inactiveHomeIcon,     AssetImages.settingsIcon,   ];   final List<String> titles = const [     Strings.projects,     Strings.home,     Strings.settings,   ];   void _onIconPressed(int newIndex) {     if (newIndex == 2) {       Navigator.pushNamed(context, RoutesName.settings);     } else {       setState(() {         index = newIndex;       });     }   }   @override   void didChangeDependencies() {     super.didChangeDependencies();     final adaptiveAdProvider =         Provider.of<AdaptiveBannerAdProvider>(context, listen: false);     adaptiveAdProvider.loadAnchoredAdaptiveAd(context);   }   @override   Widget build(BuildContext context) {     return Scaffold(       resizeToAvoidBottomInset: false,       body: Column(         children: [           Expanded(child: pages[index]),           displayAd(),           _buildBottomBar(),         ],       ),     );   }   Widget _buildBottomBar() {     return Container(       padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),       color: AppColors.bottomBarColor,       child: Row(         mainAxisAlignment: MainAxisAlignment.spaceAround,         children: List.generate(           icons.length,           (int i) => GestureDetector(             onTap: () => _onIconPressed(i),             child: _buildIconColumn(i),           ),         ),       ),     );   }   Widget _buildIconColumn(int i) {     final bool isActiveHome = index == 1 && i == 1;     final String iconPath = isActiveHome ? AssetImages.homeIcon : icons[i];     return Column(       children: [         AnimatedContainer(           duration: const Duration(milliseconds: 300),           width: 70,           height: 35,           decoration: BoxDecoration(             color: index == i ? AppColors.activeTabColor : Colors.transparent,             borderRadius: BorderRadius.circular(20),           ),           child: Padding(             padding: const EdgeInsets.all(8.0),             child: SvgPicture.asset(               iconPath,               colorFilter: ColorFilter.mode(                 index == i ? AppColors.whiteColor : AppColors.activeColor,                 BlendMode.srcIn,               ),             ),           ),         ),         const Gap(5),         Text(           titles[i],           style: TextStyle(             color: index == i ? AppColors.whiteColor : AppColors.activeColor,             fontWeight: index == i ? FontWeight.w700 : null,           ),         ),       ],     );   }   Widget displayAd() {     return Consumer<AdaptiveBannerAdProvider>(       builder: (context, adProvider, _) {         if (adProvider.isAdLoaded) {           return SizedBox(             width: adProvider.anchoredAdaptiveAd!.size.width.toDouble(),             height: adProvider.anchoredAdaptiveAd!.size.height.toDouble(),             child: AdWidget(ad: adProvider.anchoredAdaptiveAd!),           );         } else {           return Container(             color: AppColors.yellowColor,             width: adProvider.anchoredAdaptiveAd?.size.width.toDouble(),             height: adProvider.anchoredAdaptiveAd?.size.height.toDouble(),             child: Center(               child: Text(                 'Ad is Loading.......',                 style: TextStyle(color: AppColors.whiteColor),               ),             ),           );         }       },     );   }  verride   void dispose() {     Provider.of<AdaptiveBannerAdProvider>(context, listen: false).disposeAd();     super.dispose();   } }  @ 

working fine when I go to next having same process to display ad show error : Flutter :- This AdWidget is already in the Widget tree. How to disable this exception. And what does it mean?

Above Code is Working fine on HomeFeed Page. when I go to next having same process to display ad error : Flutter :- This AdWidget is already in the Widget tree. How to disable this exception. And what does it mean?

I am trying to add google native ads in my flutter app using this library

https://pub.dev/packages/google_mobile_ads

i am getting this error:

\[log\] NativeAd failed to load: LoadAdError(code: 0, domain: com.google.android.gms.ads, message: Internal error., responseInfo: ResponseInfo(responseId: _-SvZbabD5e-3LUP4oaxkAs, mediationAdapterClassName: , adapterResponses: \[AdapterResponseInfo(adapterClassName: com.google.ads.mediation.admob.AdMobAdapter, latencyMillis: 83, description: { "Adapter": "com.google.ads.mediation.admob.AdMobAdapter", "Latency": 83, "Ad Source Name": "Reservation campaign", "Ad Source ID": "7068401028668408324", "Ad Source Instance Name": "\[DevRel\] \[DO NOT EDIT\] Native Ads Campaign", "Ad Source Instance ID": "3518433842871043", "Credentials": { "pubid": "ca-app-pub-3940256099942544/2247696110/cak=no_cache&cadc=c3&caqid=_-SvZfiWDr-FmsMPtMWtgAM", "campaign_id": "12584073087" }, "Ad Error": { "Code": 0, "Message": "Internal error.", "Domain": "com.google.android.gms.ads", "Cause": "null" } }, adUnitMapping: {pubid: ca-app-pub-3940256099942544/2247696110/cak=no_cache&cadc=c3&caqid=\_-SvZfiWDr-FmsMPtMWtgAM, campaign_id: 12584073087}, adError: AdError(code: 0, domain: com.google.android.gms.ads, message: Internal error.), adSourceName: Reservation campaign, adSourceId: 7068401028668408324, adSourceInstanceName: \[DevRel\] \[DO NOT EDIT\] Native Ads Campaign, adSourceInstanceId: 3518433842871043)\], loadedAdapterResponseInfo: null), responseExtras: {mediation_group_name: Campaign}) 

Minimal Code to Reproduce

Native Controller:

import 'package:get/get.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; class NativeAdController extends GetxController{   NativeAd? ad;   final adLoaded = false.obs; } 

Ad Helper:

import 'dart:developer'; import 'package:flutter/foundation.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import '../controllers/controller_native_ads.dart'; class AdHelper{   static Future<void> initAds() async {     await MobileAds.instance.initialize();   }   void loadInterstitialAd({required VoidCallback onComplete}) {     InterstitialAd.load(       adUnitId: 'ca-app-pub-3940256099942544/1033173712',       request: const AdRequest(),       adLoadCallback: InterstitialAdLoadCallback(         // Called when an ad is successfully received.         onAdLoaded: (ad) {           ad.fullScreenContentCallback = FullScreenContentCallback(             onAdDismissedFullScreenContent: (ad) {               onComplete();             },             onAdFailedToShowFullScreenContent: (ad, error) {               log(error.toString());             },             onAdShowedFullScreenContent: (ad) {               log("Full Screen");             },           );           log('This is the $ad loaded.');         },         // Called when an ad request failed.         onAdFailedToLoad: (LoadAdError error) {           log('InterstitialAd failed to load: $error');         },       ),     );   }   static NativeAd loadNativeAd({required NativeAdController nativeAdController}) {     return NativeAd(       adUnitId: 'ca-app-pub-3940256099942544/2247696110',       listener: NativeAdListener(         onAdLoaded: (ad) {           log('$NativeAd loaded.');           nativeAdController.adLoaded.value = true;         },         onAdFailedToLoad: (ad, error) {           log('$NativeAd failed to load: $error');           ad.dispose();         },       ),       request: const AdRequest(),       // Styling       nativeTemplateStyle: NativeTemplateStyle(         templateType: TemplateType.small,       ),     )..load();   } } 

Test Screen:

import 'package:flutter/material.dart'; import '../controllers/controller_native_ads.dart'; class TestScreen extends StatelessWidget{   TestScreen({super.key});   final adController = Get.put(NativeAdController());   @override   Widget build(BuildContext context) {     adController.ad = AdHelper.loadNativeAd(nativeAdController: adController);     return Scaffold(       bottomNavigationBar: adController.ad != null && adController.adLoaded.isTrue           ? SafeArea(               child: SizedBox(                 height: 80,                 child: AdWidget(ad: adController.ad!),               ),             )           : null,     );   } } 

I am using test id provided by official google docs flutter-quick-start

Can anyone explain me why this error occurs?

We are using Google Ads as a source for new users of our app. We have it linked to Firebase and can see the install (first_open) events in Google Ad words.

We would like to record the Google Ad campaign information against our authenticated Firebase users in our back-end database so we can better track value of campaigns over time.

Is there some way to hook into the Firebase SDK in the Flutter app to listen for/retrieve this information as it is sent to Google Ads when the app is first installed? Or is there another way we can use for source attribution tracking across both iOS and Android installs?

Thanks!