Anissa  Beier

Anissa Beier

1671803295

What’s New In Gatsby

In this Gatsby article, we'll learn about What's New in Gatsby. You may have heard things about Gatsby a few years ago, and if you’re not keeping an eye on what we’re doing today, it’s entirely understandable for you to assume nothing has changed.

Naturally, given Gatsby operates in the JavaScript space, things have not only changed in the last few years, but they’ve also likely changed in the last few weeks. This is why I’ve written this article explaining what Gatsby can do today, and how I used some of our new features when upgrading my personal website: paulie.dev from Gatsby 2 to Gatsby 4.

Let’s dive in!

Server-Side Rendering (SSR)

In the fall of 2021, Gatsby launched Gatsby 4 with SSR support. I’ve used this on paulie.dev/dashboard. There are three features on my dashboard that are ideally suited to SSR

Now let’s dive into each of these and how Gatsby 4 enabled me to build them easily!

ALL REACTIONS

The reaction data is requested from a Fauna Database, and displayed in an interactive Accordion component. The reactions are first grouped by type (e.g. Happy, Cool, Tongue, and so on) and then listed by slug (URL), and a count is also displayed for each of the grouped properties.

Happy, Cool, Sad reactions order by amount and displayed in a list

All Reactions posted by visitors to paulie.dev. (Large preview)

VISITORS BY COUNTRY AND VISITORS BY LOCATION

The Visitors By Country data is requested from the new Google Analytics Data API (GA4) and listed in order by the amount of visits per country. Visitors By Location data is requested from the soon-to-be deprecated Google Analytics Core Reporting API (UA), and each location is plotted around a 3D globe that I created using three.js / @react-three/fiber. It’s made interactive using Orbit Controls from @react-three/drei. You might be wondering why I didn’t plot the GA4 data around the globe, too. The lat/long required to plot the points isn’t part of the new GA4 API. See for yourself in the GA4 Dimensions & Metrics Explorer (Built with Gatsby)

Here’s an issue on the ga-dev-tools GitHub Repo… fingers crossed it’ll get looked into at some point. 🤞

Visitors by country ordered by amount displayed next to a country flag emoji. Visitors by locations displayed as small pink markers around an interactive spinning 3D globe

All Countries and Locations of visitors to paulie.dev. (Large preview)

The data displayed in these two features is up to date as of the last time the page loaded. The Visitors By Country data will also be displayed if JavaScript is disabled in the browser. The Interactive globe, sadly, will not because three.js needs JavaScript.

SSR/CSR Hybrid

SSR is a good option for data that changes relatively quickly, but when using SSR, the data will only be “fresh” when a user first visits the page. If data were to change after the page has loaded, users would need to refresh the page to see the latest updates. This is where a hybrid SSR with CSR (Client-side request) can help.

LATEST REACTION

The Latest Reaction Feature uses this hybrid SSR/CSR approach. If Javascript is disabled in the browser, the Latest Reaction is rendered using SSR. However, if JavaScript is enabled, I poll the Fauna database using a Gatsby Serverless Function every 60 seconds and retrieve the latest reaction. This means the Latest Reaction is never more than 60 seconds out-of-date and will refresh without users needing to reload the page.

Progress timer bar decreasing before a new request is made to the database to display the emoji and url for the last reaction captured

Latest Reaction feature polling for updates every 60 seconds. (Large preview)

SSR/SSG Hybrid

Again, whilst SSR is a good option, is it always needed? Some of the data changes that occur on my site happen because of something I change. E.g I write a new post, commit the changes and trigger a build. In this instance, I’ve opted for a hybrid SSR with SSG (Static Site Generation) approach. The page is still Server-side rendered but the charts are statically generated. (Yes, a page can be both SSR and SSG.)

DATA CHARTS

I’ve used this hybrid approach and created 4 data visualizations to help me better understand the frequency with which I write and the kind of content I’m writing about, and who for. Each of these charts is populated by data from around my site that has been extracted from the frontmatter in my .mdx files and then queried from Gatsby’s data layer using GraphQL.

These charts allow me to plot or count the number of posts or articles I’ve posted each month over the last four years, the amount of posts or articles I’ve posted on each day of the week, the external publications I’ve written for (excluding Gatsby) and how many times, and then finally, a chart to show the total count for each tag used in all of the posts and articles.

Line chart displaying quantity of posts plotted over 12 months for the years, 2019, 2020, 2021 and 2022

Amount of posts by month for the last four years. (Large preview)Bar chart displaying quantity of posts plotted over each day of the week for the years, 2019, 2020, 2021 and 2022Amount of posts by day for the last four years. (Large preview)Radar chart showing quantity of posts written for external publications, and Donut chart displaying quantities of times a tag has been usedAmount of articles written for external publications and quantities of tags usage. (Large preview)

As mentioned, the dashboard of my site is Server-side rendered, but all of the above can be considered “static,” and generally speaking, when folks refer to a static site, they’re probably talking about text and images on a page.

The SSG data for the charts can’t really go out of sync because each time I write a new post or article, I commit the .mdx file to my repository, which in turn kicks off a new build in Gatsby Cloud.

This data can therefore be considered up to date as of the time the page loaded and likely won’t change during the duration of a page view.

The way I’ve created these charts uses the same technique, but because I like data, I chose to turn it into something more visually interesting. (fun fact, none of these charts were created using a charting library).

They are all hand-crafted using good ol’ mathematics, the SVG element, and with help from the following folks and their great tutorials — all will work with JavaScript disabled in the browser! 💅.

I prefer this hand-crafted approach as I found I have more control over the final output. When I’ve used charting libraries in the past I always seem to be hacking over the top of something to get the desired look, and not all charting libraries will work if JavaScript is disabled in the browser.

Deferred Static Generation (DSG)

With the release of Gatsby 4 (October 2021), Gatsby announced DSG. This page rendering method is similar to the tried and tested SSG approach (where pages are statically rendered on the server at build time), but the key difference is when.

Historically speaking, all Gatsby pages were SSG, and all pages would have to be built ahead of time. Whilst this often results in better SEO and a faster user experience than SSR, it can have adverse effects on build times.

Builds tend to fall into two main categories, and then there are a few subcategories for each:

  1. Local Development
    1. Content changes
    2. Code changes
      1. Changes that affect every page (e.g. Header/Footer)
      2. Changes that affect a single page
  2. Production Deployment.
    1. Content changes
    2. Code changes
      1. Changes that affect every page (e.g. Header/Footer)
      2. Changes that affect a single page

When developing a Gatsby site locally, it’s not always advisable to build every page, fortunately, while developing locally you probably won’t need to build all the pages.

You can use this “trick” if you like, it works a treat!: How to “fix” Gatsby’s slow local build times.

For production, however, Gatsby will need to build every page and if your content is changing quickly or you have multiple content creators working on your site, they’ll need to see the built page in a timely fashion.

Content creators typically won’t be using local .mdx files as I have on my site to write content, so Gatsby/Gatsby Cloud has a number of super fast preview options for popular CMSs such as Contentful, WordPress, Sanity, and many more!.

Specifically, in the case of production sites and content changes, DSG can be configured to defer the static generation of any page or type of page.

In short, by using DSG, Gatsby hands control of Static Site Generation over to you, the developer. By choosing which pages to defer, you have more control over your build times. After all, you know your site better than anyone, so you’ll be able to create a custom defer strategy that works best for your needs.

HOW DOES GATSBY’S DSG WORK?

Let’s start with the output. A page created using DSG is the same as a page that has been created using SSG. Meaning: it’s a fully constructed HTML page that’s been pre-built and cached on the server ahead of time and is sent to the browser when a user visits that page.

It contains all the important metadata that Google needs to index your site and since the page is pre-built, it’s super fast and provides the best user experience for end users.

When this page is rendered, however, is where DSG comes in.

If a page is deferred using DSG, then Gatsby won’t pre-build it when you deploy your site. Instead, the first time a user visits that page Gatsby will build it on the fly, or just-in-time, and then send it to the browser when it’s ready. How is this different from SSR, then?

HOW IS DSG DIFFERENT FROM SSR?

The above will only happen the first time a page is visited. When one user has visited a page once, the next user will be served an ahead-of-time pre-built SSG page from the cache, and every visitor after the first will experience the same speed as if the page were rendered using SSG.

With SSR, every visitor gets the same, sometimes slow experience, as they have to wait for the Server to generate the page before it’s sent to the browser. Typically, this leads development teams to optimize then cache headers, which is error-prone and oftentimes complex. This leads to pain for teams, and I prefer to minimize my own!

WHEN TO DEFER USING DSG

I’ve seen some really interesting defer strategies from a number of our customers. Some choose to defer pages that aren’t visited that often and this strategy is determined using Google Analytics and page view statistics. Others defer based on the date a post or article has been published; some defer based on the popularity or stock level of a product. The options you have available to you, are quite frankly endless, each business has its own use case and Gatsby is flexible enough to accommodate any and all eventualities.

Here’s a diff of the classic createPage; with DSG, you could defer all but the latest 100 posts (provided the posts have been sorted by date first, of course!)

const posts = result.data.allMdx.nodes

posts.forEach((post, index) => {
  createPage({
    path: post.slug,
    component: path.join(__dirname, `./src/templates/posts.js`),
    context: {
      id: post.id,
    },
    // index is zero-based index
+    defer: index + 1 > 100,
  })
})

…and for the curious, you can also enable DSG in our alternative page creation method: File System Route API.

export async function config() {
  return ({ params }) => {
    return {
      defer: // your defer strategy
    };
  };
}

This one small change typically results in a quite drastic reduction in build times. Of course, it depends upon the use case, but some of our customers have reduced their build times by over 50%, which means that each and every build gives them time back in their day to do more interesting things than wait for a build!

Serverless Functions

In the summer of 2021 Gatsby released Functions, I’ve used Functions for each of my; post, article, demo, or stream pages to capture visitors’ reactions.

Interface of reaction emojis and prompt asking users to click to submit how they feel.

Invite users to leave a reaction to the content. (Large preview)

Using a set of SVG emojis, I invite users to leave a reaction to my content. When any of the emojis are clicked, I post to a Serverless Function from the client with the following payload.

await fetch('/api/add-reaction', {
  method: 'POST',
  body: JSON.stringify({
    title: title,
    slug: slug,
    reaction: reaction,
    date: new Date()
  })
});

The Serverless Function, in turn, securely posts to a Fauna Database where the data is stored and ready to be retrieved and counted on the SSR page as mentioned above.

const faunadb = require('faunadb');

export default async function handler(req, res) {
  const { title, slug, reaction, date } = JSON.parse(req.body);

  const q = faunadb.query;

  const client = new faunadb.Client({ secret: process.env.FAUNA_KEY });

  try {
    await client.query(
      q.Create(q.Collection(`reactions_${process.env.NODE_ENV}`), {
        data: { title: title, slug: slug, reaction: reaction, date: date }
      })
    );
    res.status(200).json({ message: 'Lovely stuff, your reaction has been added!' });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
}

Framework Improvements

We’ve made improvements to the core framework by adding a few new APIs too! The new Script API, available from 4.15.0, among other things, comes with an off-main-thread script loading strategy which can be used to offload third-party scripts (such as Google Analytics) using Builder.io ’s Partytown 🎉.

Offloading third-party scripts to a Web Worker is a nice way to speed up page loads. I wrote a little more about this on my site: How to use Gatsby’s Script API with Google Analytics.

We also have the new Head API. Historically speaking, the recommended way to add metadata to your HTML page with Gatsby was to use react-helmet and gatsby-plugin-react-helmet.

Not anymore; from release 4.19.0, this functionality is included in the framework. I’ve written a post detailing a common migration pattern if you’re interested to know more. How to use Gatsby’s Head API with MDX.

Stay tuned for Slices API 🍕. There’s an open RFC here on the Gatsby GitHub: RFC: Slices API.

Gatsby Cloud

The Gatsby framework is free and open source and can be deployed on any number of hosting providers. We have created Gatsby Cloud as a convenient way to streamline your developer experience, with no additional plugins or configuration required. Put simply: it’s the best place to build, preview, and deploy your Gatsby site.

Whilst many in the past have complained about Gatsby’s slow build speeds, I often ask, have you tried Gatsby Cloud?

Here are some build speed benchmarks for my site, which is currently ~110 pages. These are all .mdx with the exception of the dashboard, which, as mentioned, is Server-side rendered.

Many of these pages contain code block syntax highlighting, featured and embedded images, embedded Tweets, Code Sandboxes, and YouTube videos. All of this adds to build times, not to mention MDX taking a little longer to transform than good ol’ Markdown (.md).

Note: These results are from the free tier of Gatsby Cloud (running in Pro mode for the 14-day trial).

Screenshot of Gatsby Cloud interface displaying build times for three main scenarios when a static site needs to be rebuilt

Gatsby Cloud build times (Large preview)

From bottom to top, I’ll explain the results:

  1. Triggered by Gatsby Cloud: 03.22
    This is the first time I deployed, and the site is built from a cold cache.
  2. Triggered by a manual Build: 03.48
    This is a manual build, triggered by me, and is built from a cold cache.
  3. test: content change 1: 55s
    This build was triggered by a commit to GitHub after making a change to one of my posts and is built from a warm cache.

Cold cache builds usually take longer as Gatsby Cloud has nothing to intelligently compare “what’s changed.” However, when there is a cache and the brains behind Gatsby Cloud do their thing, the build speeds are great! Personally, as a hobbyist developer, I’m fine with waiting 55 seconds for my site to be built and deployed.

(Re) Introducing Gatsby, A Reactive Site Generator

If the above isn’t enough to prove Gatsby is a lot more than simply a Static Site Generator, have a read of Gatsby’s CTO’s recent post that describes how Gatsby takes advantage of a reactive approach to regenerating a “static site” when content changes occur: (Re-) Introducing Gatsby, A Reactive Site Generator.

TLDR; When a Gatsby 4 site is deployed to Netlify or Vercel, it can only behave as if it were an SSG. When deployed to Gatsby Cloud, it can behave as though it were an RSG, decreasing build speeds in static page re-generation by 100x!

In this video, Gatsby’s CTO Kyle Mathews demonstrates Gatsby Cloud reactively generating and then deploying a static page in ~2 seconds and all from the push of a single button, our CMS previews have been able to do something similar for a while but never before has it been this fast!

Gatsby has evolved quite dramatically in the last two years. If your experience is with Gatsby 2, you may be surprised at how much faster and more flexible it has become with new page rendering modes and capabilities like DSG and SSR, and we’ve got way more updates to come!

Original article sourced at: https://www.smashingmagazine.com

#Gatsby 

What is GEEK

Buddha Community

What’s New In Gatsby
Mike  Kozey

Mike Kozey

1656151740

Test_cov_console: Flutter Console Coverage Test

Flutter Console Coverage Test

This small dart tools is used to generate Flutter Coverage Test report to console

How to install

Add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dev_dependencies:
  test_cov_console: ^0.2.2

How to run

run the following command to make sure all flutter library is up-to-date

flutter pub get
Running "flutter pub get" in coverage...                            0.5s

run the following command to generate lcov.info on coverage directory

flutter test --coverage
00:02 +1: All tests passed!

run the tool to generate report from lcov.info

flutter pub run test_cov_console
---------------------------------------------|---------|---------|---------|-------------------|
File                                         |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/                                     |         |         |         |                   |
 print_cov.dart                              |  100.00 |  100.00 |   88.37 |...,149,205,206,207|
 print_cov_constants.dart                    |    0.00 |    0.00 |    0.00 |    no unit testing|
lib/                                         |         |         |         |                   |
 test_cov_console.dart                       |    0.00 |    0.00 |    0.00 |    no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
 All files with unit testing                 |  100.00 |  100.00 |   88.37 |                   |
---------------------------------------------|---------|---------|---------|-------------------|

Optional parameter

If not given a FILE, "coverage/lcov.info" will be used.
-f, --file=<FILE>                      The target lcov.info file to be reported
-e, --exclude=<STRING1,STRING2,...>    A list of contains string for files without unit testing
                                       to be excluded from report
-l, --line                             It will print Lines & Uncovered Lines only
                                       Branch & Functions coverage percentage will not be printed
-i, --ignore                           It will not print any file without unit testing
-m, --multi                            Report from multiple lcov.info files
-c, --csv                              Output to CSV file
-o, --output=<CSV-FILE>                Full path of output CSV file
                                       If not given, "coverage/test_cov_console.csv" will be used
-t, --total                            Print only the total coverage
                                       Note: it will ignore all other option (if any), except -m
-p, --pass=<MINIMUM>                   Print only the whether total coverage is passed MINIMUM value or not
                                       If the value >= MINIMUM, it will print PASSED, otherwise FAILED
                                       Note: it will ignore all other option (if any), except -m
-h, --help                             Show this help

example run the tool with parameters

flutter pub run test_cov_console --file=coverage/lcov.info --exclude=_constants,_mock
---------------------------------------------|---------|---------|---------|-------------------|
File                                         |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/                                     |         |         |         |                   |
 print_cov.dart                              |  100.00 |  100.00 |   88.37 |...,149,205,206,207|
lib/                                         |         |         |         |                   |
 test_cov_console.dart                       |    0.00 |    0.00 |    0.00 |    no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
 All files with unit testing                 |  100.00 |  100.00 |   88.37 |                   |
---------------------------------------------|---------|---------|---------|-------------------|

report for multiple lcov.info files (-m, --multi)

It support to run for multiple lcov.info files with the followings directory structures:
1. No root module
<root>/<module_a>
<root>/<module_a>/coverage/lcov.info
<root>/<module_a>/lib/src
<root>/<module_b>
<root>/<module_b>/coverage/lcov.info
<root>/<module_b>/lib/src
...
2. With root module
<root>/coverage/lcov.info
<root>/lib/src
<root>/<module_a>
<root>/<module_a>/coverage/lcov.info
<root>/<module_a>/lib/src
<root>/<module_b>
<root>/<module_b>/coverage/lcov.info
<root>/<module_b>/lib/src
...
You must run test_cov_console on <root> dir, and the report would be grouped by module, here is
the sample output for directory structure 'with root module':
flutter pub run test_cov_console --file=coverage/lcov.info --exclude=_constants,_mock --multi
---------------------------------------------|---------|---------|---------|-------------------|
File                                         |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/                                     |         |         |         |                   |
 print_cov.dart                              |  100.00 |  100.00 |   88.37 |...,149,205,206,207|
lib/                                         |         |         |         |                   |
 test_cov_console.dart                       |    0.00 |    0.00 |    0.00 |    no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
 All files with unit testing                 |  100.00 |  100.00 |   88.37 |                   |
---------------------------------------------|---------|---------|---------|-------------------|
---------------------------------------------|---------|---------|---------|-------------------|
File - module_a -                            |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/                                     |         |         |         |                   |
 print_cov.dart                              |  100.00 |  100.00 |   88.37 |...,149,205,206,207|
lib/                                         |         |         |         |                   |
 test_cov_console.dart                       |    0.00 |    0.00 |    0.00 |    no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
 All files with unit testing                 |  100.00 |  100.00 |   88.37 |                   |
---------------------------------------------|---------|---------|---------|-------------------|
---------------------------------------------|---------|---------|---------|-------------------|
File - module_b -                            |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/                                     |         |         |         |                   |
 print_cov.dart                              |  100.00 |  100.00 |   88.37 |...,149,205,206,207|
lib/                                         |         |         |         |                   |
 test_cov_console.dart                       |    0.00 |    0.00 |    0.00 |    no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
 All files with unit testing                 |  100.00 |  100.00 |   88.37 |                   |
---------------------------------------------|---------|---------|---------|-------------------|

Output to CSV file (-c, --csv, -o, --output)

flutter pub run test_cov_console -c --output=coverage/test_coverage.csv

#### sample CSV output file:
File,% Branch,% Funcs,% Lines,Uncovered Line #s
lib/,,,,
test_cov_console.dart,0.00,0.00,0.00,no unit testing
lib/src/,,,,
parser.dart,100.00,100.00,97.22,"97"
parser_constants.dart,100.00,100.00,100.00,""
print_cov.dart,100.00,100.00,82.91,"29,49,51,52,171,174,177,180,183,184,185,186,187,188,279,324,325,387,388,389,390,391,392,393,394,395,398"
print_cov_constants.dart,0.00,0.00,0.00,no unit testing
All files with unit testing,100.00,100.00,86.07,""

Installing

Use this package as an executable

Install it

You can install the package from the command line:

dart pub global activate test_cov_console

Use it

The package has the following executables:

$ test_cov_console

Use this package as a library

Depend on it

Run this command:

With Dart:

 $ dart pub add test_cov_console

With Flutter:

 $ flutter pub add test_cov_console

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):

dependencies:
  test_cov_console: ^0.2.2

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:test_cov_console/test_cov_console.dart';

example/lib/main.dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
        // This makes the visual density adapt to the platform that you run
        // the app on. For desktop platforms, the controls will be smaller and
        // closer together (more dense) than on mobile platforms.
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Author: DigitalKatalis
Source Code: https://github.com/DigitalKatalis/test_cov_console 
License: BSD-3-Clause license

#flutter #dart #test 

Justen  Hintz

Justen Hintz

1663559281

To-do List App with HTML, CSS and JavaScript

Learn how to create a to-do list app with local storage using HTML, CSS and JavaScript. Build a Todo list application with HTML, CSS and JavaScript. Learn the basics to JavaScript along with some more advanced features such as LocalStorage for saving data to the browser.

HTML:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>To Do List With Local Storage</title>
    <!-- Font Awesome Icons -->
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
    />
    <!-- Google Fonts -->
    <link
      href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap"
      rel="stylesheet"
    />
    <!-- Stylesheet -->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="container">
      <div id="new-task">
        <input type="text" placeholder="Enter The Task Here..." />
        <button id="push">Add</button>
      </div>
      <div id="tasks"></div>
    </div>
    <!-- Script -->
    <script src="script.js"></script>
  </body>
</html>

CSS:

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
body {
  background-color: #0b87ff;
}
.container {
  width: 90%;
  max-width: 34em;
  position: absolute;
  transform: translate(-50%, -50%);
  top: 50%;
  left: 50%;
}
#new-task {
  position: relative;
  background-color: #ffffff;
  padding: 1.8em 1.25em;
  border-radius: 0.3em;
  box-shadow: 0 1.25em 1.8em rgba(1, 24, 48, 0.15);
  display: grid;
  grid-template-columns: 9fr 3fr;
  gap: 1em;
}
#new-task input {
  font-family: "Poppins", sans-serif;
  font-size: 1em;
  border: none;
  border-bottom: 2px solid #d1d3d4;
  padding: 0.8em 0.5em;
  color: #111111;
  font-weight: 500;
}
#new-task input:focus {
  outline: none;
  border-color: #0b87ff;
}
#new-task button {
  font-family: "Poppins", sans-serif;
  font-weight: 500;
  font-size: 1em;
  background-color: #0b87ff;
  color: #ffffff;
  outline: none;
  border: none;
  border-radius: 0.3em;
  cursor: pointer;
}
#tasks {
  background-color: #ffffff;
  position: relative;
  padding: 1.8em 1.25em;
  margin-top: 3.8em;
  width: 100%;
  box-shadow: 0 1.25em 1.8em rgba(1, 24, 48, 0.15);
  border-radius: 0.6em;
}
.task {
  background-color: #ffffff;
  padding: 0.3em 0.6em;
  margin-top: 0.6em;
  display: flex;
  align-items: center;
  border-bottom: 2px solid #d1d3d4;
  cursor: pointer;
}
.task span {
  font-family: "Poppins", sans-serif;
  font-size: 0.9em;
  font-weight: 400;
}
.task button {
  color: #ffffff;
  padding: 0.8em 0;
  width: 2.8em;
  border-radius: 0.3em;
  border: none;
  outline: none;
  cursor: pointer;
}
.delete {
  background-color: #fb3b3b;
}
.edit {
  background-color: #0b87ff;
  margin-left: auto;
  margin-right: 3em;
}
.completed {
  text-decoration: line-through;
}

Javascript:

//Initial References
const newTaskInput = document.querySelector("#new-task input");
const tasksDiv = document.querySelector("#tasks");
let deleteTasks, editTasks, tasks;
let updateNote = "";
let count;

//Function on window load
window.onload = () => {
  updateNote = "";
  count = Object.keys(localStorage).length;
  displayTasks();
};

//Function to Display The Tasks
const displayTasks = () => {
  if (Object.keys(localStorage).length > 0) {
    tasksDiv.style.display = "inline-block";
  } else {
    tasksDiv.style.display = "none";
  }

  //Clear the tasks
  tasksDiv.innerHTML = "";

  //Fetch All The Keys in local storage
  let tasks = Object.keys(localStorage);
  tasks = tasks.sort();

  for (let key of tasks) {
    let classValue = "";

    //Get all values
    let value = localStorage.getItem(key);
    let taskInnerDiv = document.createElement("div");
    taskInnerDiv.classList.add("task");
    taskInnerDiv.setAttribute("id", key);
    taskInnerDiv.innerHTML = `<span id="taskname">${key.split("_")[1]}</span>`;
    //localstorage would store boolean as string so we parse it to boolean back
    let editButton = document.createElement("button");
    editButton.classList.add("edit");
    editButton.innerHTML = `<i class="fa-solid fa-pen-to-square"></i>`;
    if (!JSON.parse(value)) {
      editButton.style.visibility = "visible";
    } else {
      editButton.style.visibility = "hidden";
      taskInnerDiv.classList.add("completed");
    }
    taskInnerDiv.appendChild(editButton);
    taskInnerDiv.innerHTML += `<button class="delete"><i class="fa-solid fa-trash"></i></button>`;
    tasksDiv.appendChild(taskInnerDiv);
  }

  //tasks completed
  tasks = document.querySelectorAll(".task");
  tasks.forEach((element, index) => {
    element.onclick = () => {
      //local storage update
      if (element.classList.contains("completed")) {
        updateStorage(element.id.split("_")[0], element.innerText, false);
      } else {
        updateStorage(element.id.split("_")[0], element.innerText, true);
      }
    };
  });

  //Edit Tasks
  editTasks = document.getElementsByClassName("edit");
  Array.from(editTasks).forEach((element, index) => {
    element.addEventListener("click", (e) => {
      //Stop propogation to outer elements (if removed when we click delete eventually rhw click will move to parent)
      e.stopPropagation();
      //disable other edit buttons when one task is being edited
      disableButtons(true);
      //update input value and remove div
      let parent = element.parentElement;
      newTaskInput.value = parent.querySelector("#taskname").innerText;
      //set updateNote to the task that is being edited
      updateNote = parent.id;
      //remove task
      parent.remove();
    });
  });

  //Delete Tasks
  deleteTasks = document.getElementsByClassName("delete");
  Array.from(deleteTasks).forEach((element, index) => {
    element.addEventListener("click", (e) => {
      e.stopPropagation();
      //Delete from local storage and remove div
      let parent = element.parentElement;
      removeTask(parent.id);
      parent.remove();
      count -= 1;
    });
  });
};

//Disable Edit Button
const disableButtons = (bool) => {
  let editButtons = document.getElementsByClassName("edit");
  Array.from(editButtons).forEach((element) => {
    element.disabled = bool;
  });
};

//Remove Task from local storage
const removeTask = (taskValue) => {
  localStorage.removeItem(taskValue);
  displayTasks();
};

//Add tasks to local storage
const updateStorage = (index, taskValue, completed) => {
  localStorage.setItem(`${index}_${taskValue}`, completed);
  displayTasks();
};

//Function To Add New Task
document.querySelector("#push").addEventListener("click", () => {
  //Enable the edit button
  disableButtons(false);
  if (newTaskInput.value.length == 0) {
    alert("Please Enter A Task");
  } else {
    //Store locally and display from local storage
    if (updateNote == "") {
      //new task
      updateStorage(count, newTaskInput.value, false);
    } else {
      //update task
      let existingCount = updateNote.split("_")[0];
      removeTask(updateNote);
      updateStorage(existingCount, newTaskInput.value, false);
      updateNote = "";
    }
    count += 1;
    newTaskInput.value = "";
  }
});

Related Videos

Build a Todo list app in HTML, CSS & JavaScript | JavaScript for Beginners tutorial

Build a Todo List App in HTML, CSS & JavaScript with LocalStorage | JavaScript for Beginners

To Do List using HTML CSS JavaScript | To Do List JavaScript

Create A Todo List App in HTML CSS & JavaScript | Todo App in JavaScript

#html #css #javascript

Stephie John

1606290330

What is ASO for Android & iOS Mobile App?

App Store Optimization is all about improving the visibility of a particular Android /iOS Mobile App on the App Store. Mobile App to optimize? Then go for Best SEO Company in New Zealand

How does ASO Really Work?
ASO is the process of improving the visibility of a mobile app in an app store. Just like search engine optimization (SEO) is for websites, App Store Optimization (ASO) is for mobile apps. Specifically, app store optimization includes the process of ranking highly in an app store’s search results and top charts rankings. Lia Infraservices the Top SEO Company in New Zealand and ASO marketers agrees that ranking higher in search results and top charts rankings will drive more downloads for an app.

ASO Focus on 2 Areas:
A. Search Optimization
B. Behavioral Approach

  1. Search Optimization:
    This part of App Store Optimization focuses on adjusting the name of the app and its keywords. When it comes to SEO services in New Zealand, each app is described by keywords, packed into a 100 character string. These keywords affect search rankings. The better the keywords, the higher the possibility for the app to appear in App Store search results.

/The more often the app appears in search results = the more installs /

Note: The app name is the strongest key phrase.

5 point method to Choose Keywords:
1.Create a list of general keywords based on the app description.
2.Find the Top 5 apps that target the already selected keywords.
3.Find keywords that work best for each of the 5 apps.
4.Now you should have created quite a large list of keywords. Get rid of those which don’t fit your app.
5.Create 100 characters, a comma separated string that contains the best keywords you chose.

  1. Behavioural Approach:
    App Store Optimization is not only about adjusting towards search algorithms, it’s also about human behavior. There are several areas on the Android/iOS Mobile App Store that trigger human decisions to make an install or to abandon it. These are the most important for us:

a.Application name
b.Rating
c.Screenshots / video preview
d.App description

Is your Mobile App Optimized?
When it comes to app downloads and revenue, approach the SEO Company in New Zealand, your app will do much better almost immediately after optimization. If you are interested in learning what the other factors that influence building an organic increase of app popularity are, you should get your mobile app developed by the expert SEO agency in New Zealand. Build your Android & iOS app at Lia Infraservices at cost and time effective.

#seo company in new zealand #seo services in new zealand #seo agency in new zealand #top seo company in new zealand #best seo company in new zealand #top digital marketing company in new zealand

Best App Development Companies in New York

Are you looking for a top mobile app development company in New York? Please find a list of the Best App Development Companies in New York that help to build high-quality, Robust, high-performance mobile app with advanced technology and features at an affordable price.

#best app development companies in new york #top app development companies in new york #custom app development companies in new york #leading app development companies in new york #app development companies in new york

Mobile App Development Companies in New York 2020 – TopDevelopers.co

Are you finding top mobile app development companies in New York? Hire top mobile app developers from New York selected by Topdevelopers.co by their performance.

Listing the elite and most immaculate among the top mobile app development companies and firms is our prime motto at TopDevelopers. We believe in providing the best, probably the most comprehensive companies so that our visitors could get their one-stop solutions. We comprehend in-depth analysis in the process of company selection and filter it at many levels so that only the best comes out. With the plethora of options regarding mobile app development companies available for the users, time becomes an important factor. Thus at TopDevelopers, we render simple and lucid searching options so that the visitor gets the best possible result according to their query and search.

Find Now: List of Best Mobile App Development Firms & Mobile App Developers in New York

#mobile app development service providers in new york #top mobile app development companies in new york #new york based top mobile app development firms #best mobile app developers at new york #new york state #top mobile app developers