Skip to content

Commit 086e7e8

Browse files
committed
feat(ads): add generic native ad widgets
- Implement NativeAdCardImageStart for small image at the start - Implement NativeAdCardImageTop for large image at the top - Implement NativeAdCardTextOnly for text-only ads - Use generic app_native_ad.NativeAd model for ad data - Design visually matches HeadlineTile widgets
1 parent d8313cb commit 086e7e8

File tree

3 files changed

+243
-0
lines changed

3 files changed

+243
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/native_ad.dart'
3+
as app_native_ad;
4+
import 'package:ui_kit/ui_kit.dart';
5+
6+
/// {@template native_ad_card_image_start}
7+
/// A generic widget to display a native ad with a small image at the start.
8+
///
9+
/// This widget is designed to visually match [HeadlineTileImageStart]
10+
/// and uses a generic [app_native_ad.NativeAd] model.
11+
/// {@endtemplate}
12+
class NativeAdCardImageStart extends StatelessWidget {
13+
/// {@macro native_ad_card_image_start}
14+
const NativeAdCardImageStart({
15+
required this.nativeAd,
16+
super.key,
17+
});
18+
19+
/// The generic native ad data to display.
20+
final app_native_ad.NativeAd nativeAd;
21+
22+
@override
23+
Widget build(BuildContext context) {
24+
final theme = Theme.of(context);
25+
final textTheme = theme.textTheme;
26+
final colorScheme = theme.colorScheme;
27+
28+
// Placeholder content for the generic ad.
29+
// The actual rendering of the SDK-specific ad will happen in a child widget.
30+
return Card(
31+
margin: const EdgeInsets.symmetric(
32+
horizontal: AppSpacing.paddingMedium,
33+
vertical: AppSpacing.xs,
34+
),
35+
child: Padding(
36+
padding: const EdgeInsets.all(AppSpacing.md),
37+
child: Row(
38+
crossAxisAlignment: CrossAxisAlignment.start,
39+
children: [
40+
SizedBox(
41+
width: 72, // Standard small image size
42+
height: 72,
43+
child: ClipRRect(
44+
borderRadius: BorderRadius.circular(AppSpacing.xs),
45+
child: ColoredBox(
46+
color: colorScheme.surfaceContainerHighest,
47+
child: Icon(
48+
Icons.campaign_outlined,
49+
color: colorScheme.onSurfaceVariant,
50+
size: AppSpacing.xl,
51+
),
52+
),
53+
),
54+
),
55+
const SizedBox(width: AppSpacing.md), // Always add spacing
56+
Expanded(
57+
child: Column(
58+
crossAxisAlignment: CrossAxisAlignment.start,
59+
children: [
60+
Text(
61+
'Ad: ${nativeAd.id}', // Displaying ID for now
62+
style: textTheme.titleMedium?.copyWith(
63+
fontWeight: FontWeight.w500,
64+
),
65+
maxLines: 2,
66+
overflow: TextOverflow.ellipsis,
67+
),
68+
const SizedBox(height: AppSpacing.sm),
69+
Text(
70+
'This is a generic ad placeholder.',
71+
style: textTheme.bodySmall?.copyWith(
72+
color: colorScheme.primary.withOpacity(0.7),
73+
),
74+
maxLines: 2,
75+
overflow: TextOverflow.ellipsis,
76+
),
77+
],
78+
),
79+
),
80+
],
81+
),
82+
),
83+
);
84+
}
85+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/native_ad.dart'
3+
as app_native_ad;
4+
import 'package:ui_kit/ui_kit.dart';
5+
6+
/// {@template native_ad_card_image_top}
7+
/// A generic widget to display a native ad with a large image at the top.
8+
///
9+
/// This widget is designed to visually match [HeadlineTileImageTop]
10+
/// and uses a generic [app_native_ad.NativeAd] model.
11+
/// {@endtemplate}
12+
class NativeAdCardImageTop extends StatelessWidget {
13+
/// {@macro native_ad_card_image_top}
14+
const NativeAdCardImageTop({
15+
required this.nativeAd,
16+
super.key,
17+
});
18+
19+
/// The generic native ad data to display.
20+
final app_native_ad.NativeAd nativeAd;
21+
22+
@override
23+
Widget build(BuildContext context) {
24+
final theme = Theme.of(context);
25+
final textTheme = theme.textTheme;
26+
final colorScheme = theme.colorScheme;
27+
28+
// Placeholder content for the generic ad.
29+
// The actual rendering of the SDK-specific ad will happen in a child widget.
30+
return Card(
31+
margin: const EdgeInsets.symmetric(
32+
horizontal: AppSpacing.paddingMedium,
33+
vertical: AppSpacing.xs,
34+
),
35+
child: Column(
36+
crossAxisAlignment: CrossAxisAlignment.start,
37+
children: [
38+
ClipRRect(
39+
borderRadius: const BorderRadius.only(
40+
topLeft: Radius.circular(AppSpacing.xs),
41+
topRight: Radius.circular(AppSpacing.xs),
42+
),
43+
child: Container(
44+
width: double.infinity,
45+
height: 180, // Standard large image height
46+
color: colorScheme.surfaceContainerHighest,
47+
child: Icon(
48+
Icons.campaign_outlined,
49+
color: colorScheme.onSurfaceVariant,
50+
size: AppSpacing.xxl,
51+
),
52+
),
53+
),
54+
Padding(
55+
padding: const EdgeInsets.all(AppSpacing.md),
56+
child: Column(
57+
crossAxisAlignment: CrossAxisAlignment.start,
58+
children: [
59+
Row(
60+
crossAxisAlignment: CrossAxisAlignment.start,
61+
children: [
62+
Expanded(
63+
child: Text(
64+
'Ad: ${nativeAd.id}', // Displaying ID for now
65+
style: textTheme.titleMedium?.copyWith(
66+
fontWeight: FontWeight.w500,
67+
),
68+
maxLines: 3,
69+
overflow: TextOverflow.ellipsis,
70+
),
71+
),
72+
],
73+
),
74+
const SizedBox(height: AppSpacing.sm),
75+
Text(
76+
'This is a generic ad placeholder.',
77+
style: textTheme.bodySmall?.copyWith(
78+
color: colorScheme.primary.withOpacity(0.7),
79+
),
80+
maxLines: 2,
81+
overflow: TextOverflow.ellipsis,
82+
),
83+
],
84+
),
85+
),
86+
],
87+
),
88+
);
89+
}
90+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/native_ad.dart'
3+
as app_native_ad;
4+
import 'package:ui_kit/ui_kit.dart';
5+
6+
/// {@template native_ad_card_text_only}
7+
/// A generic widget to display a native ad with text only.
8+
///
9+
/// This widget is designed to visually match [HeadlineTileTextOnly]
10+
/// and uses a generic [app_native_ad.NativeAd] model.
11+
/// {@endtemplate}
12+
class NativeAdCardTextOnly extends StatelessWidget {
13+
/// {@macro native_ad_card_text_only}
14+
const NativeAdCardTextOnly({
15+
required this.nativeAd,
16+
super.key,
17+
});
18+
19+
/// The generic native ad data to display.
20+
final app_native_ad.NativeAd nativeAd;
21+
22+
@override
23+
Widget build(BuildContext context) {
24+
final theme = Theme.of(context);
25+
final textTheme = theme.textTheme;
26+
27+
// Placeholder content for the generic ad.
28+
// The actual rendering of the SDK-specific ad will happen in a child widget.
29+
return Card(
30+
margin: const EdgeInsets.symmetric(
31+
horizontal: AppSpacing.paddingMedium,
32+
vertical: AppSpacing.xs,
33+
),
34+
child: Padding(
35+
padding: const EdgeInsets.all(AppSpacing.md),
36+
child: Row(
37+
crossAxisAlignment: CrossAxisAlignment.start,
38+
children: [
39+
Expanded(
40+
child: Column(
41+
crossAxisAlignment: CrossAxisAlignment.start,
42+
children: [
43+
Text(
44+
'Ad: ${nativeAd.id}', // Displaying ID for now
45+
style: textTheme.titleMedium?.copyWith(
46+
fontWeight: FontWeight.w500,
47+
),
48+
maxLines: 3, // Allow more lines for text-only
49+
overflow: TextOverflow.ellipsis,
50+
),
51+
const SizedBox(height: AppSpacing.sm),
52+
Text(
53+
'This is a generic ad placeholder.',
54+
style: textTheme.bodySmall?.copyWith(
55+
color: theme.colorScheme.primary.withOpacity(0.7),
56+
),
57+
maxLines: 2,
58+
overflow: TextOverflow.ellipsis,
59+
),
60+
],
61+
),
62+
),
63+
],
64+
),
65+
),
66+
);
67+
}
68+
}

0 commit comments

Comments
 (0)