nutrition_ai 0.0.1 copy "nutrition_ai: ^0.0.1" to clipboard
nutrition_ai: ^0.0.1 copied to clipboard

Passio Nutrition AI SDK for Flutter. Supports Android and iOS.

example/lib/main.dart

import 'dart:developer';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:nutrition_ai/nutrition_ai.dart';
import 'package:nutrition_ai_example/inject/injector.dart';
import 'package:nutrition_ai_example/presentation/food_search/food_search_page.dart';
import 'package:nutrition_ai_example/domain/entity/app_secret/app_secret.dart';

import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:nutrition_ai_example/router/routes.dart';

import 'package:permission_handler/permission_handler.dart';

Future<void> main() async {
  await runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();

    await Injector.setup();

    runApp(
      MaterialApp(
        localizationsDelegates: AppLocalizations.localizationsDelegates,
        supportedLocales: AppLocalizations.supportedLocales,
        localeResolutionCallback: (deviceLocale, supportedLocales) {
          if (supportedLocales
              .map((e) => e.languageCode)
              .contains(deviceLocale?.languageCode)) {
            return deviceLocale;
          } else {
            return const Locale('en', '');
          }
        },
        // Start the app with the "/" named route. In this case, the app starts
        // on the FirstScreen widget.
        initialRoute: Routes.initialRoute,
        routes: {
          // When navigating to the [Routes.foodSearchPage] route, build the [FoodSearchPage] widget.
          Routes.foodSearchPage: (context) => const FoodSearchPage(),
        },
        home: const MyApp(),
      ),
    );
  }, (error, stackTrace) async {
    if (kReleaseMode) {
      /// Here we can track our error into the crashlytics.
    } else {
      log('error: ${error.toString()}');
    }
  });
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  PassioStatus? _passioStatus;
  bool _sdkIsReady = false;

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

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;

    try {
      platformVersion = await NutritionAI.instance.version() ??
          'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    configureSDK();

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Passio SDK Plugin'),
        ),
        body: Column(
          children: [
            const SizedBox(height: 20), // Adds space of 20 units
            Center(
              child: Text('SDK Version: $_platformVersion\n'),
            ),
            Center(
              child: _passioStatus == null
                  ? const Text("Configuring SDK")
                  : Text(_passioStatus!.mode.name),
            ),
            // ElevatedButton(
            //   onPressed: () {
            //     configureSDK();
            //   },
            //   child: const Text('Configure SDK'),
            // )
            const SizedBox(height: 20),
            _sdkIsReady
                ? // Adds space of 20 units
                ElevatedButton(
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (context) => const PassioCameraDetector()),
                      );
                    },
                    child: const Text('Camera Preview'),
                  )
                : const CircularProgressIndicator(),
            const SizedBox(height: 20), // Adds space of 20 units
            _sdkIsReady
                ? ElevatedButton(
                    onPressed: () {
                      Navigator.pushNamed(context, Routes.foodSearchPage);
                    },
                    child: const Text('Text Search'),
                  )
                : const SizedBox(),

            // Text(_passioStatus?.mode.toString() ?? 'Not setup yet')
          ],
        ),
      ),
    );
  }

  void configureSDK() async {
    PassioStatus? passioStatus;
    debugPrint("configureSDK() async");
    String passioKey = AppSecret.passioKey;
    var configuration = PassioConfiguration(passioKey);
    // Before Shipping remove the debugMode line
    configuration.debugMode = -333;
    try {
      passioStatus = await NutritionAI.instance.configureSDK(configuration);
      if (passioStatus.mode == PassioMode.isReadyForDetection) {
        _sdkIsReady = true;
        print(passioStatus.activeModels);
      }
    } on PlatformException {
      passioStatus = PassioStatus();
      passioStatus.debugMessage = 'Failed to get platform version.';
    }

    setState(() {
      _passioStatus = passioStatus;
    });
  }
}

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

  @override
  State<PassioCameraDetector> createState() => _CameraDetectorState();
}

class _CameraDetectorState extends State<PassioCameraDetector>
    implements FoodRecognitionListener {
  PassioIDAttributes? _attributes;
  PlatformImage? _image;
  String? _displayedFood;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          const PassioPreview(),
          Align(
              alignment: Alignment.bottomCenter,
              child: PassioResult(attributes: _attributes, image: _image))
        ],
      ),
    );
  }

  void _checkPermission() async {
    if (await Permission.camera.request().isGranted) {
      _startFoodDetection();
    }
  }

  void _startFoodDetection() {
    var detectionConfig = FoodDetectionConfiguration();
    detectionConfig.detectBarcodes = true;
    detectionConfig.detectPackagedFood = true;
    NutritionAI.instance.startFoodDetection(detectionConfig, this);
    debugPrint("Start Food Detection from main");
  }

  @override
  void recognitionResults(FoodCandidates foodCandidates) {
    var passioID = foodCandidates.detectedCandidates.firstOrNull?.passioID;
    var barcode = foodCandidates.barcodeCandidates?.firstOrNull?.value;
    var packagedFoodCode =
        foodCandidates.packagedFoodCandidates?.firstOrNull?.packagedFoodCode;

    updateResult(passioID, barcode, packagedFoodCode);
  }

  void updateResult(PassioID? passioID, Barcode? barcode,
      PackagedFoodCode? packagedFoodCode) async {
    // if (passioID == null && barcode == null && packagedFoodCode == null) {

    // }
    if (barcode != null) {
      if (barcode != _displayedFood) {
        _attributes =
            await NutritionAI.instance.fetchAttributesForBarcode(barcode);
        _displayedFood = barcode;
        setState(() {});
        setIconState();
      }
    } else if (packagedFoodCode != null) {
      if (packagedFoodCode != _displayedFood) {
        _attributes = await NutritionAI.instance
            .fetchAttributesForPackagedFoodCode(packagedFoodCode);
        _displayedFood = packagedFoodCode;
        setIconState();
      }
    } else if (passioID != null) {
      if (passioID != _displayedFood) {
        _attributes =
            await NutritionAI.instance.lookupPassioAttributesFor(passioID);
        _displayedFood = passioID;
        setIconState();
      }
    } else {
      _attributes = null;
      _displayedFood = null;
      setIconState();
    }
  }

  void setIconState() async {
    if (_attributes == null) {
      _image = null;
      setState(() {});
      return;
    }

    var (defaultIcon, cachedIcon) = await NutritionAI.instance
        .lookupIconsFor(_attributes!.passioID, type: _attributes!.entityType);

    if (cachedIcon != null) {
      _image = cachedIcon;
      setState(() {});
      return;
    }

    _image = defaultIcon;

    setState(() {});

    var remoteIcon =
        await NutritionAI.instance.fetchIconFor(_attributes!.passioID);
    if (remoteIcon != null) {
      _image = remoteIcon;
      setState(() {});
    }
  }

  @override
  void dispose() {
    NutritionAI.instance.stopFoodDetection();
    super.dispose();
  }
}

class PassioResult extends StatelessWidget {
  final PassioIDAttributes? attributes;
  final PlatformImage? image;

  const PassioResult(
      {required this.attributes, required this.image, super.key});

  String _resultString() {
    if (attributes == null) {
      return 'Searching...';
    }

    return attributes!.name;
  }

  @override
  Widget build(BuildContext context) {
    TargetPlatform platform = defaultTargetPlatform;

    return Container(
        width: double.infinity,
        height: 100,
        margin: const EdgeInsets.all(10),
        padding: _getPadding(platform),
        decoration: const BoxDecoration(color: Colors.lightBlueAccent),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            PassioIcon(image: image),
            const SizedBox(width: 16),
            Expanded(
                child: Container(
                    alignment: Alignment.centerLeft,
                    height: 32,
                    child: FittedBox(
                      fit: BoxFit.contain,
                      child: Text(
                        _resultString(),
                        style:
                            const TextStyle(fontSize: 20, color: Colors.black),
                        softWrap: true,
                      ),
                    )))
          ],
        ));
  }

  EdgeInsets _getPadding(TargetPlatform platform) {
    switch (platform) {
      case TargetPlatform.android:
        return const EdgeInsets.symmetric(
            vertical: 24, horizontal: 16); //Marin do your magic here
      case TargetPlatform.iOS:
        return const EdgeInsets.fromLTRB(10, 10, 10, 32);
      default:
        throw UnsupportedError('Unsupported platform');
    }
  }
}


//
7
likes
0
points
528
downloads

Publisher

verified publisherpassio.ai

Weekly Downloads

Passio Nutrition AI SDK for Flutter. Supports Android and iOS.

Homepage

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on nutrition_ai