Categories
React TypeScript

Using tableLayout CSS property with TypeScript

Using table-layout property with React and TypeScript was giving a type error like below:

ERROR in /var/jenkins/workspace/dev/resources/packages/sites/table.tsx
[tsl] ERROR in /var/jenkins/workspace/dev/resources/packages/sites/table.tsx(79,25)
      TS2322: Type '{ padding: number; width: string; margin: string; tableLayout: string; }' is not assignable to type 'CSSProperties'.
  Types of property 'tableLayout' are incompatible.
    Type 'string' is not assignable to type 'TableLayout'.

This was the styling code:

const getListStyle = () => ({
    padding: 8,
    width: '100%',
    margin: '0px auto',
    tableLayout: 'auto',
});

React/Typescript JSX code:

<table style={getListStyle()}>
    <Header />
    <Body />
</table>

For some reason, table-layout doesn’t seem to accept string values. Although, as per the doc, it should be accepted.

This is the full typescript error reported by the IDE on the style attribute:

TS2322: Type ‘{ padding: number; width: string; margin: string; tableLayout: string; }’ is not assignable to type ‘CSSProperties’.   Types of property ‘tableLayout’ are incompatible.     Type ‘string’ is not assignable to type ‘TableLayout’. index.d.ts(1765, 9): The expected type comes from property ‘style’ which is declared here on type ‘DetailedHTMLProps, HTMLTableElement>’

The fix was to simply add the return type CSSProperties explicity like this:

const getListStyle = (): CSSProperties => ({
    padding: 8,
    width: '100%',
    margin: '0px auto',
    tableLayout: 'auto',
});

Categories
Web Development

CSS Flip Animation for Font Awesome Icons

This flip animation will be a perfect use-case for enable/disable UI actions.

We’ll be using rotateY transform function to achieve this flip animation.

Here is a simple HTML page with a few font awesome icons in both enabled and disabled states (we’ll be adding CSS for these states later) and jquery included for click actions.

<html>
  <head>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
  </head>
  <body>
    <i class="fa fa-envelope enabled"></i>
    <i class="fa fa-comment-alt enabled"></i>
    <i class="fa fa-camera disabled"></i>
    <i class="fa fa-bell disabled"></i>
  </body> 
</html>

Here is the CSS for enabled and disabled states with the rotateY animation.

.flip {
    animation: flip-icon 0.3s ease-in-out;
}

body {
  text-align: center; 
  width: 100%;
}

.fa {
  cursor: pointer;
  font-size: 48px; 
  margin: 24px;
}

.fa.enabled {
     color: #f44336;
 }
 .fa.disabled {
     color: grey;
 }

@keyframes flip-icon {
    0% {
        transform: rotateY(180deg);
    }
    100% {
        transform: rotateY(0deg);
    }
}

Here is the relevant click actions that will add or remove appropriate classes to start and reset the animation.

 $('body').on('click', '.fa.enabled, .fa.disabled', function iconClick() {
            const icon = $(this);
            icon.addClass('flip');
            setTimeout(() => { icon.removeClass('flip'); }, 300);
            if (icon.hasClass('enabled')) {
                setTimeout(() => {  
                  icon.removeClass('enabled');
                  icon.addClass('disabled'); 
                }, 150);
            } else {
              setTimeout(() => {  
                  icon.removeClass('disabled');
                  icon.addClass('enabled'); 
                }, 150);
            }
        });

Once you have the above code, you’ll achieve something like this:

Full Source Code can be found in this pen.

Categories
Flutter

Flutter – Local push notifications

Create a Flutter project

To create a new project in Flutter execute the following command line in the terminal.

flutter create tryoutflutter

We are using tryoutflutter as the project name. It will create a folder with the name of the project. Inside the project, the files structure should like this:

Let’s look at the folders and files in the project.

android folder: this includes specific files to configure our Android application.

  • To get special permissions,
  • Firebase configuration,
  • To set a logo, splash screen, display name, etc,.

iOS folder, this includes specific files to configure our iOS application.

Build folder, this includes the debug application build APK. This will automatically create once you run the project.

lib folder, this includes Dart files. In this folder, we need to write and manage Dart code.

pubspec.yaml, in this file we can manage the packages (libraries), assets (images, icons, etc,.) that we will use for our application.

Dependencies

Have to install the following packages,

  • RxDart this package adds the additional capabilities to receive the sequence of events (stream) and controls (stream controllers),
  • Flutter local notifications to compose, schedule and manage notifications.

Android setup

Add an event receiver to receive scheduled notification while the application was not running in the foreground.

Paste the following code in AndroidManifest.xml file which is located in this <projectName>/android/app/src/main path.

<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            </intent-filter>
        </receiver>

iOS setup

For iOS add this code snippet in AppDelegate.swift file to receive notification, which is located in the following path <projectname>/ios/Runner/AppleDelegate.swift and also have to get permission from user to receive notifications.

To know more about UNUserNotificationCenter,

if #available(iOS 10.0, *) {
   UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}

Trigger Local Notifications

  • For iOS devices have to get permissions to receive notifications from our applications,
void _requestPermissions() {
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            MacOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
  }
  • Call _requestPermissions() from initState() if the platform is iOS, the initState( ) will called once the widget is initialized,
@override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      _requestPermissions();
    }
  }
  • After those steps, we are ready to compose and receive notifications locally, let see an example to display a quick notification.
  • This will be used as a welcome greeting message during user onboard, or an alert if the event triggers.
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject =
    BehaviorSubject<ReceivedNotification>();
final BehaviorSubject<String> selectNotificationSubject =
    BehaviorSubject<String>();
NotificationAppLaunchDetails notificationAppLaunchDetails;

class ReceivedNotification {
  final int id;
  final String title;
  final String body;
  final String payload;
  ReceivedNotification({
    @required this.id,
    @required this.title,
    @required this.body,
    @required this.payload,
  });
}
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  notificationAppLaunchDetails =
      await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
  var initializationSettingsAndroid =
      AndroidInitializationSettings('ic_launcher');
  var initializationSettingsIOS = IOSInitializationSettings(
      requestAlertPermission: false,
      requestBadgePermission: false,
      requestSoundPermission: false,
      onDidReceiveLocalNotification:
          (int id, String title, String body, String payload) async {
        didReceiveLocalNotificationSubject.add(ReceivedNotification(
            id: id, title: title, body: body, payload: payload));
      });
  var initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
  await flutterLocalNotificationsPlugin.initialize(initializationSettings,
      onSelectNotification: (String payload) async {
    if (payload != null) {
      debugPrint('notification payload: ' + payload);
    }
    selectNotificationSubject.add(payload);
  });
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter local push notification'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _requestPermissions() {
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            MacOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
  }
  @override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      _requestPermissions();
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                onPressed: () {
                  showNotification();
                },
                child: Text("Show notification"),
              )
            ],
          ),
        ));
  }
}
Future<void> showNotification() async {
  const AndroidNotificationDetails androidPlatformChannelSpecifics =
  AndroidNotificationDetails(
    'your channel id', 
    'your channel name', 
    'your channel description',
    importance: Importance.max,
    priority: Priority.high,
    ticker: 'ticker'
  );
  
  const NotificationDetails platformChannelSpecifics = NotificationDetails(
    android: androidPlatformChannelSpecifics,
  );
  await flutterLocalNotificationsPlugin.show(
    1, 
    'plain title', 
    "Test Message", 
    platformChannelSpecifics,
    payload: 'item x'
  );
}
  • Function to display a scheduled notification, have to pass the following parameters channelId need to be unique, channelName, title, description, dateTime.
//ScheduleNotification
Future<void> scheduleNotificationFunction( String channelId, String channelName, String channelDes, String title, String description, DateTime dateTime) async {
  print("Notification schduled for $channelId");

  var platformChannelSpecifics = NotificationDetails(
      AndroidNotificationDetails(channelId, channelName, channelDes,
          playSound: true, enableLights: true, enableVibration: true),
      IOSNotificationDetails());

  await flutterLocalNotificationsPlugin.schedule(
      0, title, description, dateTime, platformChannelSpecifics);
}
  • Function to display a daily repeat notification, have to pass the following parameters channelId need to be unique, channelName, title, description, onlyTime 24 hour format.
//Daily repeat notification
Future<void> _repeatNotificationDailyAtSpecificTime() async {
  const AndroidNotificationDetails androidPlatformChannelSpecifics =
      AndroidNotificationDetails('repeating channel id',
          'repeating channel name', 'repeating description');
  const NotificationDetails platformChannelSpecifics =
      NotificationDetails(android: androidPlatformChannelSpecifics);
// ignore: deprecated_member_use
  await flutterLocalNotificationsPlugin.showDailyAtTime(2443, "Good Morning",
      "Message body", Time(10, 00, 00), platformChannelSpecifics);
}
  • This function to cancel a scheduled notification requires channelId
// To cancel notification
Future<void> _cancelNotification(id) async {
  await flutterLocalNotificationsPlugin.cancel(id);
}

Android

iOS

I have attached the complete code in this GitHub repo.