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

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

example/lib/main.dart

import 'dart:async';
//import 'dart:developer';
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:nutrition_ai/nutrition_ai.dart';
import 'package:nutrition_ai_example/const/dimens.dart';
import 'package:nutrition_ai_example/domain/entity/app_secret/app_secret.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/presentation/static_image/static_image_page.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(
      ScreenUtilInit(
          designSize: const Size(Dimens.designWidth, Dimens.designHeight),
          minTextAdapt: true,
          splitScreenMode: true,
          builder: (context, child) {
            return 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(),
                Routes.staticImagePage: (context) => const StaticImagePage(),
              },
              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.getSDKVersion() ??
          '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: () {
                testTags();
              },
              child: const Text('test tags'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                iconURLFor();
              },
              child: const Text('test iconURLFor'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                testTransform();
              },
              child: const Text('test transformCGRectForm'),
            ),
            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(),
            const SizedBox(height: 20), // Adds space of 20 units
            _sdkIsReady
                ? ElevatedButton(
                    onPressed: () {
                      Navigator.pushNamed(context, Routes.staticImagePage);
                    },
                    child: const Text('Static image'),
                  )
                : 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, debugMode: -333);
    passioStatus = await NutritionAI.instance.configureSDK(configuration);
    if (passioStatus.mode == PassioMode.isReadyForDetection) {
      _sdkIsReady = true;
    }

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

  void testTags() async {
    var pidAtt =
        await NutritionAI.instance.fetchAttributesForBarcode("5411188118961");
    debugPrint(
        "1111 The right result for 5411188118961 is [vegan, vegetarian, gluten free] == ${pidAtt?.foodItem!.tags.toString()}");
    var pidAtt2 =
        await NutritionAI.instance.fetchAttributesForBarcode("753656713038");
    debugPrint(
        "2222 There are no tags 753656713038 = ${pidAtt2?.foodItem?.tags.toString()} ");

    var tags = await NutritionAI.instance.fetchTagsFor("1603211380195");
    debugPrint(
        "33332 The right result for 1603211380195 is [vegan, vegetarian, gluten free]] == ${tags?.toString()}");

    var tags2 = await NutritionAI.instance.fetchTagsFor("BAK0049");
    debugPrint(
        "4444 The right result for BAK0049 is empty ${tags2?.toString()}");
  }

  void iconURLFor() async {
    var urlString = await NutritionAI.instance.iconURLFor("usda45296835");
    debugPrint("link for usda45296835 $urlString");
  }

  void testTransform() async {
    var boundingBox = const Rectangle(0.14928516745567322, 0.29599857330322266,
        0.7111189961433411, 0.4279291033744812);
    var toRect = const Rectangle(200.0, 200.0, 400.0, 400.0);
    var result =
        await NutritionAI.instance.transformCGRectForm(boundingBox, toRect);
    debugPrint("transformCGRectForm result =   $result");
  }
}

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;
  double? _volumeEstimate;

  @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,
                  volumeEstimate: _volumeEstimate))
        ],
      ),
    );
  }

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

  void _startFoodDetection() {
    var detectionConfig = FoodDetectionConfiguration(
        detectBarcodes: true, detectPackagedFood: true);
    if (defaultTargetPlatform == TargetPlatform.iOS) {
      detectionConfig.volumeDetectionMode = VolumeDetectionMode.auto;
    }
    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;

    var volumeEstimate = foodCandidates
        .detectedCandidates.firstOrNull?.amountEstimate?.volumeEstimate;

    updateResult(passioID, barcode, packagedFoodCode, volumeEstimate);
  }

  void updateResult(PassioID? passioID, Barcode? barcode,
      PackagedFoodCode? packagedFoodCode, double? volumeEstimate) 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 || volumeEstimate != _volumeEstimate) {
        _attributes =
            await NutritionAI.instance.lookupPassioAttributesFor(passioID);
        _displayedFood = passioID;
        _volumeEstimate = volumeEstimate;
        setIconState();
      }
    } else {
      _attributes = null;
      _displayedFood = null;
      setIconState();
    }
  }

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

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

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

    _image = passioIcons.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;
  final double? volumeEstimate;

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

  String _resultString() {
    if (attributes == null) {
      return 'Searching...';
    }
    if (volumeEstimate?.toInt() != null) {
      String volume = volumeEstimate!.toInt().toString();
      return attributes!.name + " vol= " + volume;
    }
    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