Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A problem with storing icon #19

Closed
merskip opened this issue Nov 2, 2020 · 10 comments
Closed

A problem with storing icon #19

merskip opened this issue Nov 2, 2020 · 10 comments

Comments

@merskip
Copy link

merskip commented Nov 2, 2020

Hello,

I wanted to use icons as a symbol of a category. I need to store the selected icon in a database. The simplest way is store fields: codePoint, fontFamily, fontPackage and matchTextDirection. This solution is suggested in README file and in a few Stackoverflow posts but this doesn't work. After some time the icons are different.

In REDME there is an example:
IconData(0xe3af, fontFamily: 'MaterialIcons'); // Icons.camera

but now under 0xe3af is:
static const IconData record_voice_over_outlined = IconData(0xe3af, fontFamily: 'MaterialIcons');

This causes changing icons after some time. In the database, I store the above four fields. This means we cannot trust values for a long time. Using the code Icons.camera is ok and works for all time.

I think we need to store icons in a different way.

I think something like that:

  Map<String, String> iconToMap(IconPack iconPack, IconData icon) {
    if (iconPack == IconPack.material) {
      final iconEntry =
          icons.entries.firstWhere((iconEntry) => iconEntry.value == icon); // icons from src/material.icons.dart
      return {
        'pack': "material",
        'key': iconEntry.key,
      };
    }
    // etc.
  }
@merskip
Copy link
Author

merskip commented Nov 3, 2020

The full version of serializing and deserializing look like this:

import 'package:flutter/material.dart';
import 'package:flutter_iconpicker/IconPicker/Packs/Cupertino.dart'
    as Cupertino;
import 'package:flutter_iconpicker/IconPicker/Packs/FontAwesome.dart'
    as FontAwesome;
import 'package:flutter_iconpicker/IconPicker/Packs/LineIcons.dart'
    as LineAwesome;
import 'package:flutter_iconpicker/IconPicker/Packs/Material.dart' as Material;
import 'package:flutter_iconpicker/IconPicker/Packs/MaterialOutline.dart'
    as MaterialOutline;
import 'package:flutter_iconpicker/Models/IconPack.dart';

Map<String, dynamic> serializeIcon(IconData icon, {IconPack iconPack}) {
  if (iconPack == null) {
    if (icon.fontFamily == "MaterialIcons")
      iconPack = IconPack.material;
    else if (icon.fontFamily == "outline_material_icons")
      iconPack = IconPack.materialOutline;
    else if (icon.fontFamily == "CupertinoIcons")
      iconPack = IconPack.cupertino;
    else if (icon.fontPackage == "font_awesome_flutter")
      iconPack = IconPack.fontAwesomeIcons;
    else if (icon.fontPackage == "line_awesome_flutter")
      iconPack = IconPack.lineAwesomeIcons;
    else
      iconPack = IconPack.custom;
  }
  switch (iconPack) {
    case IconPack.material:
      return {
        'pack': "material",
        'key': _getIconKey(Material.icons, icon),
      };
    case IconPack.materialOutline:
      return {
        'pack': "materialOutline",
        'key': _getIconKey(MaterialOutline.materialOutline, icon),
      };
    case IconPack.cupertino:
      return {
        'pack': "cupertino",
        'key': _getIconKey(Cupertino.cupertinoIcons, icon),
      };
    case IconPack.fontAwesomeIcons:
      return {
        'pack': "fontAwesomeIcons",
        'key': _getIconKey(FontAwesome.fontAwesomeIcons, icon),
      };
      break;
    case IconPack.lineAwesomeIcons:
      return {
        'pack': "lineAwesomeIcons",
        'key': _getIconKey(LineAwesome.lineAwesomeIcons, icon),
      };
      break;
    case IconPack.custom:
      return {
        'pack': "custom",
        'iconData': {
          'codePoint': icon.codePoint,
          'fontFamily': icon.fontFamily,
          'fontPackage': icon.fontPackage,
          'matchTextDirection': icon.matchTextDirection,
        }
      };
    default:
      return null;
  }
}

IconData deserializeIcon(Map<String, dynamic> iconMap) {
  try {
    final pack = iconMap['pack'];
    final iconKey = iconMap['key'];
    switch (pack) {
      case "material":
        return Material.icons[iconKey];
      case "materialOutline":
        return MaterialOutline.materialOutline[iconKey];
      case "cupertino":
        return Cupertino.cupertinoIcons[iconKey];
      case "fontAwesomeIcons":
        return FontAwesome.fontAwesomeIcons[iconKey];
      case "lineAwesomeIcons":
        return LineAwesome.lineAwesomeIcons[iconKey];
      case "custom":
        final iconData = iconMap['iconData'];
        return IconData(
          iconData['codePoint'],
          fontFamily: iconData['fontFamily'],
          fontPackage: iconData['fontPackage'],
          matchTextDirection: iconData['matchTextDirection'],
        );
      default:
        return null;
    }
  } catch (e) {
    return null;
  }
}

String _getIconKey(Map<String, IconData> icons, IconData icon) =>
    icons.entries.firstWhere((iconEntry) => iconEntry.value == icon).key;

Nice would be optimize serializing for a large number of it.

Example of the result:

{
   "pack": "material",
   "key": "camera"
}

@Ahmadre
Copy link
Owner

Ahmadre commented May 11, 2021

The full version of serializing and deserializing look like this:

import 'package:flutter/material.dart';

import 'package:flutter_iconpicker/IconPicker/Packs/Cupertino.dart'

    as Cupertino;

import 'package:flutter_iconpicker/IconPicker/Packs/FontAwesome.dart'

    as FontAwesome;

import 'package:flutter_iconpicker/IconPicker/Packs/LineIcons.dart'

    as LineAwesome;

import 'package:flutter_iconpicker/IconPicker/Packs/Material.dart' as Material;

import 'package:flutter_iconpicker/IconPicker/Packs/MaterialOutline.dart'

    as MaterialOutline;

import 'package:flutter_iconpicker/Models/IconPack.dart';



Map<String, dynamic> serializeIcon(IconData icon, {IconPack iconPack}) {

  if (iconPack == null) {

    if (icon.fontFamily == "MaterialIcons")

      iconPack = IconPack.material;

    else if (icon.fontFamily == "outline_material_icons")

      iconPack = IconPack.materialOutline;

    else if (icon.fontFamily == "CupertinoIcons")

      iconPack = IconPack.cupertino;

    else if (icon.fontPackage == "font_awesome_flutter")

      iconPack = IconPack.fontAwesomeIcons;

    else if (icon.fontPackage == "line_awesome_flutter")

      iconPack = IconPack.lineAwesomeIcons;

    else

      iconPack = IconPack.custom;

  }

  switch (iconPack) {

    case IconPack.material:

      return {

        'pack': "material",

        'key': _getIconKey(Material.icons, icon),

      };

    case IconPack.materialOutline:

      return {

        'pack': "materialOutline",

        'key': _getIconKey(MaterialOutline.materialOutline, icon),

      };

    case IconPack.cupertino:

      return {

        'pack': "cupertino",

        'key': _getIconKey(Cupertino.cupertinoIcons, icon),

      };

    case IconPack.fontAwesomeIcons:

      return {

        'pack': "fontAwesomeIcons",

        'key': _getIconKey(FontAwesome.fontAwesomeIcons, icon),

      };

      break;

    case IconPack.lineAwesomeIcons:

      return {

        'pack': "lineAwesomeIcons",

        'key': _getIconKey(LineAwesome.lineAwesomeIcons, icon),

      };

      break;

    case IconPack.custom:

      return {

        'pack': "custom",

        'iconData': {

          'codePoint': icon.codePoint,

          'fontFamily': icon.fontFamily,

          'fontPackage': icon.fontPackage,

          'matchTextDirection': icon.matchTextDirection,

        }

      };

    default:

      return null;

  }

}



IconData deserializeIcon(Map<String, dynamic> iconMap) {

  try {

    final pack = iconMap['pack'];

    final iconKey = iconMap['key'];

    switch (pack) {

      case "material":

        return Material.icons[iconKey];

      case "materialOutline":

        return MaterialOutline.materialOutline[iconKey];

      case "cupertino":

        return Cupertino.cupertinoIcons[iconKey];

      case "fontAwesomeIcons":

        return FontAwesome.fontAwesomeIcons[iconKey];

      case "lineAwesomeIcons":

        return LineAwesome.lineAwesomeIcons[iconKey];

      case "custom":

        final iconData = iconMap['iconData'];

        return IconData(

          iconData['codePoint'],

          fontFamily: iconData['fontFamily'],

          fontPackage: iconData['fontPackage'],

          matchTextDirection: iconData['matchTextDirection'],

        );

      default:

        return null;

    }

  } catch (e) {

    return null;

  }

}



String _getIconKey(Map<String, IconData> icons, IconData icon) =>

    icons.entries.firstWhere((iconEntry) => iconEntry.value == icon).key;

Nice would be optimize serializing for a large number of it.

Example of the result:

{

   "pack": "material",

   "key": "camera"

}

hey @merskip sorry for my late response. I was very busy since last year and I have to investigate into this asap.

I see here a crucial bug, where I already got some ideas and yeah: your solution looks promising!

I'll write you here back, as soon as I finished the work on it :)

@leonardarnold
Copy link

As far as I know it's only about the material design code points, they could change at any time. This is quite annoying - not sure how other packages handle code points.

@Ahmadre
Copy link
Owner

Ahmadre commented May 19, 2021

@merskip @leonardarnold

just published a new version: Version

Thank you so much @merskip your solution is exactly what was needed :). Works like a charm.

I also added a complete example and you can test it yourself with the new example project.

@Ahmadre Ahmadre closed this as completed May 19, 2021
@merskip
Copy link
Author

merskip commented May 20, 2021

Thank you for fixing it!

@bambinoua
Copy link

It is good to implement new functionality for icon serialization but it would be also good to notify users about breaking changes. I used method iconDataToMap/mapToIconData and after upgrage the package I got an error because you removed those ones and created news ones. Please update change log.

@leonardarnold
Copy link

thank you @Ahmadre i will check it out later! : - )

@Ahmadre
Copy link
Owner

Ahmadre commented May 20, 2021

It is good to implement new functionality for icon serialization but it would be also good to notify users about breaking changes. I used method iconDataToMap/mapToIconData and after upgrage the package I got an error because you removed those ones and created news ones. Please update change log.

Oh yeah, I just updated the README, but I will also update the Changelog and mark a big "Breaking Change" section 👍🏼

@Ahmadre
Copy link
Owner

Ahmadre commented May 20, 2021

It is good to implement new functionality for icon serialization but it would be also good to notify users about breaking changes. I used method iconDataToMap/mapToIconData and after upgrage the package I got an error because you removed those ones and created news ones. Please update change log.

Breaking Change is now mentioned in the changelog. Thank you for the reminder here :)

@plekedev
Copy link

plekedev commented Jul 5, 2021

There is another breaking change in my case, for previously stored icons there is no pack defined and the function deserializeIcon returns null
Sample of stored icon: {codePoint: 62948, fontFamily: FontAwesomeSolid, fontPackage: font_awesome_flutter, matchTextDirection: false}

I fixed it by creating an IconData from the data if deserializeIcon returns null. I think this should be the default for deserializeIcon to handle previously saved icons.

IconData base64ToIconData(String iconJson) {
  IconData iconDataResult;
  if (iconJson == null || iconJson == '') {
    iconDataResult = Icons.block;
  } else {
    Map<String, dynamic> data = json.decode(utf8.decode(base64.decode(iconJson)));
    iconDataResult = deserializeIcon(data);
    if (iconDataResult == null) {
      iconDataResult = IconData(
        data['codePoint'],
        fontFamily: data['fontFamily'],
        fontPackage: data['fontPackage'],
        matchTextDirection: data['matchTextDirection'],
      );
    }
    if (iconDataResult == null) {
      iconDataResult = Icons.block;
    }
  }
  return iconDataResult;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants