lumi_h5p 1.1.0 copy "lumi_h5p: ^1.1.0" to clipboard
lumi_h5p: ^1.1.0 copied to clipboard

A Flutter plugin to view and serve H5P content locally using a static server and InAppWebView.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:lumi_h5p/config.dart';
import 'package:lumi_h5p/controllers/h5p_controller.dart';
import 'package:lumi_h5p/models/h5p_request_model.dart';

// priority url with their refnames
const String h5pUrl1 =
    'https://rmnzqinspzgmvgxistyi.supabase.co/storage/v1/object/sign/h5p/test/Interactive%20Video.h5p?token=eyJraWQiOiJzdG9yYWdlLXVybC1zaWduaW5nLWtleV9lYTlmZWZkMS01MGQxLTQzZDgtOGUxMC1lNjBiZmNlZmNmMWMiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJoNXAvdGVzdC9JbnRlcmFjdGl2ZSBWaWRlby5oNXAiLCJpYXQiOjE3NjE1MDM4NTksImV4cCI6MTc5MzAzOTg1OX0.qMAJYEY4IsrCjhQnFFlz2jA-H0OBJyJtXiwsj5nL35k';
const String h5pUrl2 =
    'https://rmnzqinspzgmvgxistyi.supabase.co/storage/v1/object/sign/h5p/test/Test%20mcq.h5p?token=eyJraWQiOiJzdG9yYWdlLXVybC1zaWduaW5nLWtleV9lYTlmZWZkMS01MGQxLTQzZDgtOGUxMC1lNjBiZmNlZmNmMWMiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJoNXAvdGVzdC9UZXN0IG1jcS5oNXAiLCJpYXQiOjE3NjE0OTk1OTMsImV4cCI6MTc5MzAzNTU5M30.Fb4dOMKXjTB47Ht1ot7PLcsw6qHbDWJ5FSZL8Q5Meq8';
const String h5pUrl3 =
    'https://rmnzqinspzgmvgxistyi.supabase.co/storage/v1/object/sign/h5p/test/Course%20Presentation.h5p?token=eyJraWQiOiJzdG9yYWdlLXVybC1zaWduaW5nLWtleV9lYTlmZWZkMS01MGQxLTQzZDgtOGUxMC1lNjBiZmNlZmNmMWMiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJoNXAvdGVzdC9Db3Vyc2UgUHJlc2VudGF0aW9uLmg1cCIsImlhdCI6MTc2MjA5MzYyMSwiZXhwIjoxNzkzNjI5NjIxfQ.uguBKJCrO3O1-lnt-DIFT3LBZrwL_oAoX7LK2VUgll8';
const String h5pUrl4 =
    'https://rmnzqinspzgmvgxistyi.supabase.co/storage/v1/object/sign/h5p/test/Example%20content%20-%20Arts%20of%20Europe.h5p?token=eyJraWQiOiJzdG9yYWdlLXVybC1zaWduaW5nLWtleV9lYTlmZWZkMS01MGQxLTQzZDgtOGUxMC1lNjBiZmNlZmNmMWMiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJoNXAvdGVzdC9FeGFtcGxlIGNvbnRlbnQgLSBBcnRzIG9mIEV1cm9wZS5oNXAiLCJpYXQiOjE3NjIwOTM2NDgsImV4cCI6MTc5MzYyOTY0OH0.5XnGMY5n0jB4rynD8UcKTBAmAAN0bw1MnNdsrcX2qmg';

const Map<String, String> h5pUrls = {
  'h5purl1': h5pUrl1,
  'h5purl2': h5pUrl2,
  'h5purl3': h5pUrl3,
  'h5purl4': h5pUrl4,
};

List<H5PRequestModel> h5pmodels = [
  H5PRequestModel(
    refName: 'h5purl1',
    url: h5pUrl1,
    priority: 6,
  ), // priority set it to be dowanloded first
  H5PRequestModel(refName: 'h5purl4', url: h5pUrl4, priority: 7),
];

List<H5PRequestModel> moreh5pmodels = [
  H5PRequestModel(refName: 'h5purl2', url: h5pUrl2),
  H5PRequestModel(refName: 'h5purl3', url: h5pUrl3),
  H5PRequestModel(refName: 'h5purl4', url: h5pUrl4),
];

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: TestView());
  }
}

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

  @override
  State<TestView> createState() => _TestViewState();
}

class _TestViewState extends State<TestView> {
  final Set<H5PRequestModel> _selectedRequests = {};
  final LumiH5PController _h5pcontroller = LumiH5PController();
  @override
  void initState() {
    _h5pcontroller.addRequestList(h5pmodels);
    //h5pDebug = true; // 👈 enable debug logs
    h5pError = true; // 👈 enable error logs
    //h5pPort=8050; // 👈 set custom port if needed

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    H5pWebView webView = H5pWebView(
      controller: _h5pcontroller,
      listenToEvents:
          true, //only needed if you want to listen to events like marks and anything else
      onXApiEvent: (event) {
        h5pLog(message: "📢 xAPI Event: $event");
      },
    );
    return Scaffold(
      appBar: AppBar(title: const Text("H5P Viewer Example")),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(12),
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: h5pUrls.entries.map((entry) {
                  return Container(
                    margin: const EdgeInsets.only(right: 10),
                    padding: const EdgeInsets.all(8),
                    decoration: BoxDecoration(
                      color: Colors.blue.shade50,
                      borderRadius: BorderRadius.circular(12),
                      border: Border.all(color: Colors.blue.shade200),
                    ),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        ElevatedButton(
                          onPressed: () {
                            _h5pcontroller.loadH5P(url: entry.value);
                          },
                          child: Text(entry.key),
                        ),
                        const SizedBox(height: 6),
                        OutlinedButton(
                          onPressed: () {
                            _h5pcontroller.loadH5P(
                              url: entry.value,
                            ); //pass ref name only if already dwonlaoded
                          },
                          child: Text("Without Ref (${entry.key})"),
                        ),
                      ],
                    ),
                  );
                }).toList(),
              ),
            ),
          ),

          // -----------------------------
          // LOADER SECTION
          // -----------------------------
          ValueListenableBuilder<H5PLoadStatus>(
            valueListenable: _h5pcontroller.status,
            builder: (_, status, _) {
              if (status == H5PLoadStatus.downloading) {
                return Column(
                  children: [
                    const Text("Downloading..."),
                    ValueListenableBuilder<double>(
                      valueListenable: _h5pcontroller
                          .downloadProgress, //get progress of downloaded file
                      builder: (_, progress, _) =>
                          LinearProgressIndicator(value: progress),
                    ),
                  ],
                );
              } else if (status == H5PLoadStatus.extracting) {
                return Column(
                  children: [
                    const Text("Extracting... Please wait"),
                    ValueListenableBuilder<double>(
                      valueListenable: _h5pcontroller
                          .extractProgress, //get progress of extratced file
                      builder: (_, progress, _) => LinearProgressIndicator(
                        value: progress > 0 ? progress : null,
                      ),
                    ),
                  ],
                );
              }
              return const SizedBox.shrink();
            },
          ),

          // -----------------------------
          // WEBVIEW
          // -----------------------------
          Expanded(child: webView),

          // -----------------------------
          // REQUEST LIST WITH SELECTION
          // -----------------------------
          SizedBox(
            height: 220,
            child: Column(
              children: [
                // REMOVE SELECTED BUTTON
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 12),
                  child: Row(
                    children: [
                      ElevatedButton.icon(
                        onPressed: () {
                          List<H5PRequestModel> selected = _selectedRequests
                              .toList();
                          if (selected.isNotEmpty) {
                            _h5pcontroller.addRequestList(
                              selected,
                              remove: true,
                            );
                            setState(() => _selectedRequests.clear());
                          }
                        },
                        icon: const Icon(Icons.delete),
                        label: const Text("Remove Selected"),
                      ),

                      Spacer(),
                      TextButton(
                        onPressed: () {
                          _h5pcontroller
                              .clearAllRequests(); //remove all h5p files
                        },
                        child: Text("Clear ALL"),
                      ),
                    ],
                  ),
                ),

                const SizedBox(height: 6),

                // LIST
                Expanded(
                  child: ValueListenableBuilder<List<H5PRequestModel>>(
                    valueListenable: _h5pcontroller.requests,
                    builder: (context, list, _) {
                      if (list.isEmpty) return const Text("No requests");

                      return ListView.builder(
                        itemCount: list.length,
                        itemBuilder: (_, index) {
                          final req = list[index];
                          final isSelected = _selectedRequests.contains(req);

                          return ListTile(
                            leading: Checkbox(
                              value: isSelected,
                              onChanged: (_) {
                                setState(() {
                                  isSelected
                                      ? _selectedRequests.remove(req)
                                      : _selectedRequests.add(req);
                                });
                              },
                            ),
                            title: Row(
                              children: [
                                Text(req.refName),
                                Spacer(),
                                Icon(_getStatusIcon(req.status)),
                              ],
                            ),
                            subtitle: Row(
                              children: [Text("Status: ${req.status.name}")],
                            ),
                            trailing: IconButton(
                              icon: const Icon(Icons.delete),
                              onPressed: () {
                                _h5pcontroller.addRequest(
                                  req,
                                  remove: true,
                                ); // remove and clear memory
                              },
                            ),
                          );
                        },
                      );
                    },
                  ),
                ),
              ],
            ),
          ),

          TextButton(
            onPressed: () => _h5pcontroller.addRequestList(
              moreh5pmodels,
            ), //add more h5p files to download
            child: const Text("Add more H5P requests"),
          ),
        ],
      ),
    );
  }

  IconData _getStatusIcon(H5PFileStatus status) {
    switch (status) {
      case H5PFileStatus.undefined:
        return Icons.hourglass_empty;
      case H5PFileStatus.downloading:
        return Icons.downloading;

      case H5PFileStatus.downloaded:
        return Icons.check_circle;
      case H5PFileStatus.failed:
        return Icons.error;
      default:
        return Icons.info;
    }
  }
}
2
likes
150
points
37
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin to view and serve H5P content locally using a static server and InAppWebView.

License

MIT (license)

Dependencies

archive, dio, flutter, flutter_inappwebview, mocktail, path_provider, permission_handler, shelf, shelf_static, webview_flutter

More

Packages that depend on lumi_h5p