adaptive_gamification 0.1.1 copy "adaptive_gamification: ^0.1.1" to clipboard
adaptive_gamification: ^0.1.1 copied to clipboard

A deployment-oriented Flutter runtime library for deterministic execution of exported adaptive policy decisions.

example/lib/main.dart

import 'package:adaptive_gamification/adaptive_gamification.dart';
import 'package:flutter/material.dart';

import 'package:adaptive_gamification/src/utils/difficulty_mapper.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Adaptive Gamification Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.indigo,
      ),
      home: const DemoHomePage(),
    );
  }
}

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

  @override
  State<DemoHomePage> createState() => _DemoHomePageState();
}

class _DemoHomePageState extends State<DemoHomePage> {
  final AdaptiveEngine _engine = AdaptiveEngine();

  bool _loading = true;
  String _error = '';

  int _difficultyIndex = 2;
  int _streak = 0;
  int _total = 0;
  int _correct = 0;
  double _responseTime = 4.0;

  AdaptiveDecision? _lastDecision;

  @override
  void initState() {
    super.initState();
    _initEngine();
  }

  Future<void> _initEngine() async {
    try {
      await _engine.initFromAsset(
        policyAssetPath: 'assets/adaptive_policy.json',
      );

      if (!mounted) return;
      setState(() {
        _loading = false;
        _error = '';
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _loading = false;
        _error = e.toString();
      });
    }
  }

  double get _accuracy => ((_correct + 1) / (_total + 2)).clamp(0.0, 1.0);

  String _difficultyLabelFromIndex(int index) {
    return DifficultyMapper.indexToString(index);
  }

  int _difficultyIndexFromLabel(String label) {
    return DifficultyMapper.stringToIndex(label);
  }

  int _computeNextDifficultyIndex(String nextDifficulty) {
    final int target = _difficultyIndexFromLabel(nextDifficulty);
    final int current = _difficultyIndex;

    if (target == current) return current;
    if (target > current) return (current + 1).clamp(0, 4);
    return (current - 1).clamp(0, 4);
  }

  Future<void> _answer(bool isCorrect) async {
    setState(() {
      _total += 1;
      if (isCorrect) {
        _correct += 1;
        _streak += 1;
      } else {
        _streak = 0;
      }
    });

    final userState = UserState(
      currentDifficultyIndex: _difficultyIndex,
      accuracy: _accuracy,
      responseTime: _responseTime,
      correctStreak: _streak,
    );

    final AdaptiveDecision decision = _engine.decide(userState);
    final int nextDifficultyIndex =
    _computeNextDifficultyIndex(decision.nextDifficulty);

    setState(() {
      _lastDecision = decision;
      _difficultyIndex = nextDifficultyIndex;
    });
  }

  void _resetSession() {
    setState(() {
      _difficultyIndex = 2;
      _streak = 0;
      _total = 0;
      _correct = 0;
      _responseTime = 4.0;
      _lastDecision = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_loading) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }

    if (_error.isNotEmpty) {
      return Scaffold(
        appBar: AppBar(title: const Text('Adaptive Gamification Example')),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: SelectableText(
            'Failed to initialize engine:\n\n$_error',
            style: const TextStyle(fontSize: 14),
          ),
        ),
      );
    }

    final String currentDifficulty = _difficultyLabelFromIndex(_difficultyIndex);
    final Map<String, dynamic> metadata = _engine.metadata;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Adaptive Gamification Example'),
        actions: [
          IconButton(
            onPressed: _resetSession,
            icon: const Icon(Icons.refresh),
            tooltip: 'Reset session',
          ),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _SectionCard(
            title: 'Policy Information',
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _InfoRow(
                  label: 'Initialized',
                  value: _engine.isInitialized ? 'Yes' : 'No',
                ),
                _InfoRow(
                  label: 'Structured Format',
                  value: _engine.isStructuredFormat ? 'Yes' : 'No',
                ),
                _InfoRow(
                  label: 'Policy Size',
                  value: _engine.policySize.toString(),
                ),
                _InfoRow(
                  label: 'Grid',
                  value: _engine.grid.toStringAsFixed(2),
                ),
                _InfoRow(
                  label: 'Format Version',
                  value: '${metadata['format_version'] ?? '-'}',
                ),
                _InfoRow(
                  label: 'Policy Type',
                  value: '${metadata['policy_type'] ?? '-'}',
                ),
                _InfoRow(
                  label: 'Export Mode',
                  value: '${metadata['export_mode'] ?? '-'}',
                ),
                _InfoRow(
                  label: 'Export Resolution',
                  value: '${metadata['export_resolution'] ?? '-'}',
                ),
                _InfoRow(
                  label: 'State Decimals',
                  value: '${metadata['state_decimals'] ?? '-'}',
                ),
                _InfoRow(
                  label: 'Num Actions',
                  value: '${metadata['num_actions'] ?? '-'}',
                ),
              ],
            ),
          ),
          const SizedBox(height: 12),
          _SectionCard(
            title: 'Current Session State',
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _InfoRow(label: 'Difficulty', value: currentDifficulty),
                _InfoRow(
                  label: 'Accuracy',
                  value: '${(_accuracy * 100).toStringAsFixed(1)}%',
                ),
                _InfoRow(label: 'Correct Streak', value: _streak.toString()),
                _InfoRow(label: 'Answered', value: _total.toString()),
                _InfoRow(label: 'Correct', value: _correct.toString()),
              ],
            ),
          ),
          const SizedBox(height: 12),
          _SectionCard(
            title: 'Response Time (seconds)',
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  _responseTime.toStringAsFixed(2),
                  style: Theme.of(context).textTheme.titleMedium,
                ),
                Slider(
                  value: _responseTime,
                  min: 1.0,
                  max: 20.0,
                  divisions: 38,
                  label: _responseTime.toStringAsFixed(2),
                  onChanged: (value) {
                    setState(() {
                      _responseTime = value;
                    });
                  },
                ),
                const Text(
                  'This simulates how quickly the learner responds. Lower response time generally indicates higher responsiveness.',
                ),
              ],
            ),
          ),
          const SizedBox(height: 12),
          _SectionCard(
            title: 'Latest Adaptive Decision',
            child: _lastDecision == null
                ? const Text(
              'Answer a question to generate the first adaptive decision.',
            )
                : Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _HighlightedDecision(
                  nextDifficulty: _lastDecision!.nextDifficulty,
                ),
                const SizedBox(height: 10),
                _InfoRow(label: 'Reason', value: _lastDecision!.reason),
                _InfoRow(
                  label: 'Support Strategy',
                  value: _lastDecision!.supportStrategy ?? '-',
                ),
                _InfoRow(
                  label: 'Source Action',
                  value: _lastDecision!.sourceActionName ?? '-',
                ),
                _InfoRow(
                  label: 'Decision Type',
                  value: _lastDecision!.decisionType ?? '-',
                ),
                _InfoRow(
                  label: 'Lookup Key',
                  value: _lastDecision!.lookupKey ?? '-',
                ),
                _InfoRow(
                  label: 'Exact Match',
                  value: _lastDecision!.foundExactMatch
                      ? 'Yes'
                      : 'No (fallback)',
                ),
              ],
            ),
          ),
          const SizedBox(height: 16),
          Row(
            children: [
              Expanded(
                child: FilledButton.icon(
                  onPressed: () => _answer(true),
                  icon: const Icon(Icons.check_circle_outline),
                  label: const Text('Correct'),
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: FilledButton.tonalIcon(
                  onPressed: () => _answer(false),
                  icon: const Icon(Icons.cancel_outlined),
                  label: const Text('Wrong'),
                ),
              ),
            ],
          ),
          const SizedBox(height: 12),
          const Text(
            'Note: this example applies at most one visible difficulty-level step per answer in the UI. The exported policy still provides the target recommendation, but the demo smooths transitions for readability.',
          ),
        ],
      ),
    );
  }
}

class _SectionCard extends StatelessWidget {
  final String title;
  final Widget child;

  const _SectionCard({
    required this.title,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 0.5,
      child: Padding(
        padding: const EdgeInsets.all(14),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(title, style: Theme.of(context).textTheme.titleMedium),
            const SizedBox(height: 10),
            child,
          ],
        ),
      ),
    );
  }
}

class _InfoRow extends StatelessWidget {
  final String label;
  final String value;

  const _InfoRow({
    required this.label,
    required this.value,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 6),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 130,
            child: Text(label),
          ),
          Expanded(
            child: SelectableText(
              value,
              style: const TextStyle(fontWeight: FontWeight.w600),
            ),
          ),
        ],
      ),
    );
  }
}

class _HighlightedDecision extends StatelessWidget {
  final String nextDifficulty;

  const _HighlightedDecision({
    required this.nextDifficulty,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(12),
        color: Theme.of(context).colorScheme.primaryContainer,
      ),
      child: Row(
        children: [
          const Icon(Icons.psychology_alt_outlined),
          const SizedBox(width: 10),
          Expanded(
            child: Text(
              'Recommended next difficulty: $nextDifficulty',
              style: Theme.of(context).textTheme.titleSmall,
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
0
points
89
downloads

Publisher

unverified uploader

Weekly Downloads

A deployment-oriented Flutter runtime library for deterministic execution of exported adaptive policy decisions.

Repository (GitHub)
View/report issues

Topics

#adaptive-learning #reinforcement-learning #flutter-package #gamification #runtime-policy

License

unknown (license)

Dependencies

flutter

More

Packages that depend on adaptive_gamification