novvy_ads 1.0.0-beta.33 copy "novvy_ads: ^1.0.0-beta.33" to clipboard
novvy_ads: ^1.0.0-beta.33 copied to clipboard

Flutter plugin for Novvy Ads SDK.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';

import 'package:novvy_ads/novvy_ads.dart';

void main() {
  runApp(const NovvyAdsExampleApp());
}

class NovvyAdsExampleApp extends StatelessWidget {
  const NovvyAdsExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Novvy Ads Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF6C5CE7),
        brightness: Brightness.light,
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorSchemeSeed: const Color(0xFF6C5CE7),
        brightness: Brightness.dark,
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> implements NovvyAdsCallback {
  bool _initialized = false;
  bool _initializing = false;

  final _appIdController = TextEditingController(text: 'your_app_id');
  final _endpointController = TextEditingController(text: 'your_end_point',);
  final _apiKeyController = TextEditingController(text: 'your_api_key');
  final _interstitialAdUnitController = TextEditingController(text: 'your_ad_unit_id');
  final _rewardedAdUnitController = TextEditingController(text: 'your_ad_unit_id');
  final _bannerAdUnitController = TextEditingController(text: 'your_ad_unit_id');

  bool _showBanner = false;
  _DemoPlayerAdapter _demoPlayer = _DemoPlayerAdapter();

  final _logs = <_LogEntry>[];

  void _addLog(String message, {_LogLevel level = _LogLevel.info}) {
    setState(() {
      _logs.insert(0, _LogEntry(message: message, level: level));
      if (_logs.length > 200) _logs.removeLast();
    });
  }

  Future<void> _initialize() async {
    if (_initializing) return;
    setState(() => _initializing = true);

    _addLog('Initializing Novvy SDK...');
    try {
      await NovvyAds.initialize(
        config: NovvyInitConfig(
          appId: _appIdController.text.trim(),
          endpoint: _endpointController.text.trim(),
          apiKey: _apiKeyController.text.trim(),
        ),
        callback: this,
      );
      _addLog('SDK initialized successfully', level: _LogLevel.success);
      setState(() => _initialized = true);
    } catch (e) {
      _addLog('SDK initialization failed: $e', level: _LogLevel.error);
    } finally {
      setState(() => _initializing = false);
    }
  }

  void _showInterstitial() {
    final adUnitId = _interstitialAdUnitController.text.trim();
    _addLog('Requesting interstitial: $adUnitId');
    NovvyAds.showInterstitial(adUnitId);
  }

  void _showRewarded() {
    final adUnitId = _rewardedAdUnitController.text.trim();
    _addLog('Requesting rewarded: $adUnitId');
    NovvyAds.showRewarded(adUnitId);
  }

  void _toggleBanner() {
    setState(() {
      _showBanner = !_showBanner;
      if (_showBanner) {
        // Reset player so position starts from 0 for the new banner session.
        _demoPlayer = _DemoPlayerAdapter();
      }
    });
    _addLog(
      _showBanner ? 'Banner added to widget tree' : 'Banner removed from widget tree',
      level: _LogLevel.info,
    );
  }

  void _updateContext() {
    NovvyAds.updateContext(
      NovvyContextConfig(
        seriesName: 'your_series_name',
        episodeNumber: 1, // your_episode_number
        contentUrl: 'your_content_url',
      ),
    );
    _addLog('Context updated', level: _LogLevel.success);
  }

  // ── NovvyAdsCallback ──

  @override
  void onInterstitialDismissed(String adUnitId) {
    _addLog('Interstitial dismissed: $adUnitId', level: _LogLevel.success);
  }

  @override
  void onInterstitialFailed(String adUnitId, String error) {
    _addLog('Interstitial failed [$adUnitId]: $error', level: _LogLevel.error);
  }

  @override
  void onRewardedDismissed(String adUnitId, NovvyReward? reward) {
    if (reward != null) {
      _addLog(
        'Rewarded dismissed [$adUnitId]: earned ${reward.amount} ${reward.type}',
        level: _LogLevel.success,
      );
    } else {
      _addLog(
        'Rewarded dismissed [$adUnitId]: no reward (user skipped)',
        level: _LogLevel.warning,
      );
    }
  }

  @override
  void onRewardedFailed(String adUnitId, String error) {
    _addLog('Rewarded failed [$adUnitId]: $error', level: _LogLevel.error);
  }

  @override
  void onBannerImpression(String adUnitId) {
    _addLog('Banner impression (callback): $adUnitId', level: _LogLevel.success);
  }

  @override
  void onBannerFailed(String adUnitId, String error) {
    _addLog('Banner failed (callback) [$adUnitId]: $error', level: _LogLevel.error);
  }

  @override
  void onBannerClosed(String adUnitId) {
    _addLog('Banner closed (callback): $adUnitId');
  }

  @override
  void onFeedAdReady(String adUnitId, NovvyFeedAdProvider provider) {
    _addLog('Feed ad ready: $adUnitId', level: _LogLevel.success);
  }

  @override
  void onFeedAdFailed(String adUnitId, String error) {
    _addLog('Feed ad failed [$adUnitId]: $error', level: _LogLevel.error);
  }

  @override
  void onFeedAdPlaybackTick(String adUnitId, int remainingSeconds) {
    _addLog('Feed ad tick [$adUnitId]: ${remainingSeconds}s remaining');
  }

  @override
  void onFeedAdImpression(String adUnitId) {
    _addLog('Feed ad impression: $adUnitId', level: _LogLevel.success);
  }

  @override
  void onFeedAdClicked(String adUnitId) {
    _addLog('Feed ad clicked: $adUnitId');
  }

  @override
  void dispose() {
    _appIdController.dispose();
    _endpointController.dispose();
    _apiKeyController.dispose();
    _interstitialAdUnitController.dispose();
    _rewardedAdUnitController.dispose();
    _bannerAdUnitController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Novvy Ads Demo'),
        centerTitle: true,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // Platform badge
          Center(
            child: Chip(
              avatar: Icon(
                Platform.isAndroid ? Icons.android : Icons.apple,
                size: 18,
              ),
              label: Text(Platform.isAndroid ? 'Android' : 'iOS'),
            ),
          ),
          const SizedBox(height: 16),

          // Init config
          _SectionCard(
            title: 'SDK Configuration',
            icon: Icons.settings,
            children: [
              _ConfigField(
                label: 'App ID',
                controller: _appIdController,
                enabled: !_initialized,
              ),
              _ConfigField(
                label: 'Endpoint',
                controller: _endpointController,
                enabled: !_initialized,
              ),
              _ConfigField(
                label: 'API Key',
                controller: _apiKeyController,
                enabled: !_initialized,
                obscure: true,
              ),
              const SizedBox(height: 12),
              FilledButton.icon(
                onPressed: _initialized || _initializing ? null : _initialize,
                icon: _initializing
                    ? SizedBox.square(
                        dimension: 18,
                        child: CircularProgressIndicator(
                          strokeWidth: 2,
                          color: colorScheme.onPrimary,
                        ),
                      )
                    : Icon(_initialized ? Icons.check : Icons.rocket_launch),
                label: Text(
                  _initialized
                      ? 'Initialized'
                      : _initializing
                          ? 'Initializing...'
                          : 'Initialize SDK',
                ),
              ),
            ],
          ),
          const SizedBox(height: 16),

          // Ad controls
          _SectionCard(
            title: 'Ad Controls',
            icon: Icons.play_circle_outline,
            children: [
              _ConfigField(
                label: 'Interstitial Ad Unit ID',
                controller: _interstitialAdUnitController,
              ),
              const SizedBox(height: 8),
              Row(
                children: [
                  Expanded(
                    child: FilledButton.tonalIcon(
                      onPressed: _initialized ? _showInterstitial : null,
                      icon: const Icon(Icons.fullscreen),
                      label: const Text('Show Interstitial'),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 16),
              _ConfigField(
                label: 'Rewarded Ad Unit ID',
                controller: _rewardedAdUnitController,
              ),
              const SizedBox(height: 8),
              Row(
                children: [
                  Expanded(
                    child: FilledButton.tonalIcon(
                      onPressed: _initialized ? _showRewarded : null,
                      icon: const Icon(Icons.card_giftcard),
                      label: const Text('Show Rewarded'),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 16),
              OutlinedButton.icon(
                onPressed: _initialized ? _updateContext : null,
                icon: const Icon(Icons.tune),
                label: const Text('Update Context'),
              ),
            ],
          ),
          const SizedBox(height: 16),

          // Banner ad controls
          _SectionCard(
            title: 'Banner Ad',
            icon: Icons.view_agenda_outlined,
            children: [
              _ConfigField(
                label: 'Banner Ad Unit ID',
                controller: _bannerAdUnitController,
              ),
              const SizedBox(height: 8),
              FilledButton.tonalIcon(
                onPressed: _initialized ? _toggleBanner : null,
                icon: Icon(_showBanner ? Icons.visibility_off : Icons.visibility),
                label: Text(_showBanner ? 'Remove Banner' : 'Show Banner'),
              ),
              const SizedBox(height: 12),
              // Banner container — simulates a video player area
              Container(
                width: 260,
                height: 600,
                decoration: BoxDecoration(
                  border: Border.all(
                    color: colorScheme.outlineVariant,
                  ),
                  borderRadius: BorderRadius.circular(8),
                ),
                clipBehavior: Clip.antiAlias,
                child: Stack(
                  children: [
                    // Test button — positioned to overlap with banner area
                    Positioned(
                      bottom: 110,
                      right: 0,
                      child: TextButton(
                        onPressed: () => _addLog(
                          'Test button tapped!',
                          level: _LogLevel.warning,
                        ),
                        child: const Text('Test'),
                      ),
                    ),
                    // Banner — one line to create a fully managed banner
                    if (_showBanner)
                      NovvyAds.banner(
                        adUnitId: _bannerAdUnitController.text.trim(),
                        player: _demoPlayer,
                      ),
                  ],
                ),
              ),
            ],
          ),
          const SizedBox(height: 16),

          // Event log
          _SectionCard(
            title: 'Event Log',
            icon: Icons.receipt_long,
            trailing: _logs.isNotEmpty
                ? TextButton(
                    onPressed: () => setState(_logs.clear),
                    child: const Text('Clear'),
                  )
                : null,
            children: [
              if (_logs.isEmpty)
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 24),
                  child: Center(
                    child: Text(
                      'No events yet.\nInitialize the SDK and trigger an ad.',
                      textAlign: TextAlign.center,
                      style: theme.textTheme.bodyMedium?.copyWith(
                        color: colorScheme.onSurfaceVariant,
                      ),
                    ),
                  ),
                )
              else
                ...List.generate(
                  _logs.length.clamp(0, 50),
                  (i) => _LogTile(entry: _logs[i]),
                ),
            ],
          ),
          const SizedBox(height: 32),
        ],
      ),
    );
  }
}

// ── Helper widgets ──

class _SectionCard extends StatelessWidget {
  final String title;
  final IconData icon;
  final Widget? trailing;
  final List<Widget> children;

  const _SectionCard({
    required this.title,
    required this.icon,
    this.trailing,
    required this.children,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Card(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(color: theme.colorScheme.outlineVariant),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Row(
              children: [
                Icon(icon, size: 20, color: theme.colorScheme.primary),
                const SizedBox(width: 8),
                Expanded(
                  child: Text(title, style: theme.textTheme.titleMedium),
                ),
                ?trailing,
              ],
            ),
            const SizedBox(height: 12),
            ...children,
          ],
        ),
      ),
    );
  }
}

class _ConfigField extends StatelessWidget {
  final String label;
  final TextEditingController controller;
  final bool enabled;
  final bool obscure;

  const _ConfigField({
    required this.label,
    required this.controller,
    this.enabled = true,
    this.obscure = false,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: TextField(
        controller: controller,
        enabled: enabled,
        obscureText: obscure,
        style: const TextStyle(fontSize: 13),
        decoration: InputDecoration(
          labelText: label,
          isDense: true,
          border: const OutlineInputBorder(),
        ),
      ),
    );
  }
}

class _LogTile extends StatelessWidget {
  final _LogEntry entry;

  const _LogTile({required this.entry});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final (icon, color) = switch (entry.level) {
      _LogLevel.info => (Icons.info_outline, theme.colorScheme.onSurface),
      _LogLevel.success => (Icons.check_circle_outline, Colors.green),
      _LogLevel.warning => (Icons.warning_amber, Colors.orange),
      _LogLevel.error => (Icons.error_outline, Colors.redAccent),
    };

    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 3),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Icon(icon, size: 16, color: color),
          const SizedBox(width: 8),
          Expanded(
            child: Text(
              entry.message,
              style: theme.textTheme.bodySmall?.copyWith(color: color),
            ),
          ),
        ],
      ),
    );
  }
}

// ── Models ──

enum _LogLevel { info, success, warning, error }

class _LogEntry {
  final String message;
  final _LogLevel level;
  final DateTime timestamp;

  _LogEntry({
    required this.message,
    this.level = _LogLevel.info,
  }) : timestamp = DateTime.now();
}

/// A simple player adapter for demo purposes that simulates playback.
class _DemoPlayerAdapter extends NovvyPlayerAdapter {
  final _start = DateTime.now();

  @override
  int get currentPositionMs =>
      DateTime.now().difference(_start).inMilliseconds;

  @override
  bool get isPlaying => true;
}
0
likes
140
points
846
downloads

Documentation

API reference

Publisher

verified publishernovvy.ai

Weekly Downloads

Flutter plugin for Novvy Ads SDK.

Homepage

License

MIT (license)

Dependencies

crypto, flutter, meta, plugin_platform_interface

More

Packages that depend on novvy_ads

Packages that implement novvy_ads