Posts in category “Programming”

Best Practices for Handling Time Zones in Daylight Saving Regions

Managing time zones can be tricky, especially in regions that observe daylight saving time (DST). Using the right time zone identifiers can simplify your work by automatically adjusting for these changes. Here’s a quick guide to best practices for handling time zones, using examples from New Zealand and Australia.

The Problem with Fixed Time Zones

The Problem with Fixed Time Zones Using fixed time zones like NZST (New Zealand Standard Time) typically doesn't account for daylight saving changes, potentially leading to incorrect time calculations during daylight saving periods. However, certain platforms, such as .NET, which handles these transitions internally, giving the correct local time. This behavior might not be consistent across different platforms and systems.

The Best Practice Solution

The best practice is to use time zone identifiers that handle both standard and daylight saving times automatically. These identifiers switch between standard time and daylight saving time based on the current date, keeping your times accurate year-round. For example:

  • New Zealand: Use Pacific/Auckland.
  • Australia: Use Australia/Sydney.
  • United States: Use America/New_York for Eastern Time, America/Los_Angeles for Pacific Time, etc.

These identifiers know when to transition between standard time and daylight saving time, ensuring your applications reflect the correct local time.

Troubleshooting State Propagation Issues with `ChangeNotifierProvider` in Flutter

In the HappyNotes project, I encountered a perplexing issue with the NoteModel not updating as expected across flutter widgets. Despite initializing NoteModel with initial values and using ChangeNotifierProvider, the state changes weren’t reflecting in the model when I try to collect data back. This article outlines the troubleshooting process and solution, which can be a valuable guide for other developers facing similar challenges.

Background

In HappyNotes project, the NewNote widget is where users could create notes. Each note had properties like isPrivate and isMarkdown, managed through a NoteModel using ChangeNotifier. Here’s a simplified version of the relevant parts of my setup:

class NoteModel extends ChangeNotifier {
  bool isPrivate;
  bool isMarkdown;

  NoteModel({this.isPrivate = true, this.isMarkdown = false});

  set isPrivate(bool value) {
    _isPrivate = value;
    notifyListeners();
  }

  set isMarkdown(bool value) {
    _isMarkdown = value;
    notifyListeners();
  }
}

The NewNote widget utilized ChangeNotifierProvider to provide the NoteModel to its children:

class NewNote extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => NoteModel(),
      child: Scaffold(
        appBar: AppBar(title: Consumer<NoteModel>(
          builder: (context, noteModel, child) {
            return Text(noteModel.isPrivate ? 'Private Note' : 'Public Note');
          },
        )),
        body: NoteEditor(),
      ),
    );
  }
}

The Problem

After setting the initial values for isPrivate and isMarkdown, later changes to these properties weren’t being reflected in the model when I retrieve the status data when saving a note . I tried various approaches, including manually calling notifyListeners and using setState within event handlers, but nothing solved the issue.

Troubleshooting Process

  1. Double Check Initialization: First, I verified that NoteModel was being initialized correctly with the ChangeNotifierProvider. The initial values were correct, but subsequent changes weren’t propagating.

  2. Use of Consumer:

I ensured that widgets depending on NoteModel used Consumer to listen for changes:

Consumer<NoteModel>(
  builder: (context, noteModel, child) {
    return Switch(
      value: noteModel.isPrivate,
      onChanged: (value) {
        noteModel.isPrivate = value;
      },
    );
  },
);
  1. Spotting out the root cause: Multiple providers initialization:

Finally I found that having multiple ChangeNotifierProviders at different levels of the widget tree, is the root cause that leads to this issue. I shouldn't initialize the same model twice at the root level and at the page level.

The Solution

The breakthrough came when we adjusted our main application setup to use a single ChangeNotifierProvider for NoteModel:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => NoteModel(),
      child: MyApp(),
    ),
  );
}

In NewNote, I removed the redundant ChangeNotifierProvider and directly used the globally provided NoteModel:

class NewNote extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Consumer<NoteModel>(
          builder: (context, noteModel, child) {
            return Text(noteModel.isPrivate ? 'Private Note' : 'Public Note');
          },
        ),
      ),
      body: NoteEditor(),
    );
  }
}

By centralizing NoteModel provisioning, state changes propagated correctly across the app. This resolved the issue, proving that a single ChangeNotifierProvider is the key for consistent state management in Flutter.

Key Takeaways

  • Use a Single ChangeNotifierProvider: Ensure a single source of truth for state by providing your model at a high level in the widget tree.
  • Leverage Consumer for Efficient Updates: Use Consumer to automatically rebuild parts of the UI that depend on the model, reducing manual state management.

Best Practices for Using `Provider` in Flutter Apps

Provider is a powerful state management solution in Flutter that allows for efficient and organized management of app state. In this guide, we'll explore some best practices for using Provider to keep your Flutter apps clean and maintainable.

Why Use Provider?

Provider offers a simple, scalable way to handle state management in Flutter apps. It's ideal for sharing and managing state across widgets, especially in large applications where state needs to be accessed from various parts of the app.

1. Utilize MultiProvider for Multiple Models

When your app requires multiple state models, initializing each one individually can become cumbersome. Instead, use MultiProvider to group your models together efficiently. This keeps your main function neat and ensures that all your models are readily available throughout the app.

Example:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:your_app/models/note_model.dart';
import 'package:your_app/models/user_model.dart';
import 'package:your_app/screens/home_screen.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => NoteModel()),
        ChangeNotifierProvider(create: (context) => UserModel()),
      ],
      child: const YourApp(),
    ),
  );
}

In this setup, both NoteModel and UserModel are initialized at the root level, making them accessible throughout your app.

2. Scope Providers Appropriately

Avoid providing all your models at the root level unless they are needed throughout the entire app. For state that is only relevant to specific sections or widgets, provide the ChangeNotifier closer to where it's needed. This reduces unnecessary rebuilds and makes your app more efficient.

Example:

class SomeFeatureScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => SomeFeatureModel(),
      child: SomeFeatureWidget(),
    );
  }
}

In this example, SomeFeatureModel is provided only for SomeFeatureScreen, ensuring it's scoped to the relevant part of the app.

3. Avoid Duplication

Ensure each ChangeNotifier is provided only once at the appropriate level to avoid duplication. Multiple instances of the same model can lead to inconsistent state and unexpected behavior.

Example:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => UserModel()),
        // Avoid duplicating UserModel in nested widgets
      ],
      child: MaterialApp(
        home: HomeScreen(),
      ),
    );
  }
}

4. Use Provider for Static Data

Not all models need to be ChangeNotifier. For data that doesn’t change or doesn’t need to notify listeners, use Provider. This is useful for static data or configurations.

Example:

Provider(create: (context) => SomeStaticData()),

5. Access Models Wisely

Use context.watch<T>() to get notified of changes and rebuild when the state changes, or context.read<T>() to read the value without rebuilding. This ensures that your widgets only rebuild when necessary, keeping your app performance optimal.

Example:

class ExampleWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final noteModel = context.watch<NoteModel>();
    final userModel = context.read<UserModel>();

    return Column(
      children: [
        Text('Note is private: ${noteModel.isPrivate}'),
        Text('User name: ${userModel.name}'),
      ],
    );
  }
}

Summary

Using Provider effectively involves:

  1. Grouping models with MultiProvider: This keeps your main function clean and ensures that all necessary models are provided.
  2. Scoping models appropriately: Provide models where they are needed to avoid unnecessary rebuilds.
  3. Avoiding duplication: Ensure each model is only provided once at the appropriate level.
  4. Using Provider for static data: This avoids unnecessary rebuilds and keeps your app efficient.
  5. Accessing models wisely: Use the appropriate methods to read or watch the state without causing unnecessary rebuilds.

Quick Guide: Building an API Server with Express and JWT Authentication

In this tutorial, I'll walk you through setting up a simple API server using Express and express-jwt for JSON Web Token (JWT) authentication. By the end, you'll have a functional server that can issue JWTs and verify them for secure endpoints. Let's dive in!

Step 1: Setup and Install Dependencies

First, create a new project directory and initialize a Node.js project. Then, install the required packages:

mkdir express-jwt-api
cd express-jwt-api
npm init -y
npm install express express-jwt jsonwebtoken body-parser

Step 2: Create the Basic Server

Create a server.js file to set up your Express server:

const express = require('express');
const bodyParser = require('body-parser');
const routes = require('./routes');

const app = express();
app.use(bodyParser.json());
app.use('/api', routes);

const PORT = 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

This code sets up an Express server and uses body-parser to parse JSON requests.

Step 3: Define Routes

Create a routes.js file for defining our API routes. We'll add a login route to generate JWTs and a protected route that requires JWT verification.

const express = require('express');
const jwt = require('jsonwebtoken');
const { verifyToken } = require('./middleware');

const router = express.Router();

// Login route to generate JWT
router.post('/login', (req, res) => {
    const { username, password } = req.body;
    if (username === 'testuser' && password === 'testpassword') {
        const user = { id: 1, username: 'testuser' };
        const token = jwt.sign(user, 'your_secret_key', { expiresIn: '1h' });
        res.json({ token });
    } else {
        res.status(401).json({ message: 'Invalid credentials' });
    }
});

// Protected route
router.get('/protected', verifyToken, (req, res) => {
    console.log('req.user:', req.user);
    if (req.user && req.user.id) {
        res.json({ message: 'Protected route accessed.', userId: req.user.id });
    } else {
        res.status(401).json({ message: 'Unauthorized' });
    }
});

router.get('/public', (req, res) => res.send('This is a public route.'));

module.exports = router;

Step 4: Implement JWT Verification

Create a middleware.js file to define the JWT verification middleware:

const { expressjwt } = require('express-jwt');

const verifyToken = expressjwt({
    secret: 'your_secret_key',
    algorithms: ['HS256'],
    requestProperty: 'user'
});

module.exports = { verifyToken };

This middleware verifies the JWT and attaches the decoded payload to req.user.

Step 5: Test Your API

  1. Start the server:

    node server.js
    
  2. Generate a JWT by sending a POST request to /api/login with a JSON body { "username": "testuser", "password": "testpassword" }. You'll get a token in response.

  3. Use the token to access the protected route:

    curl -H "Authorization: Bearer YOUR_JWT" http://localhost:3000/api/protected
    

    You should see a response with the user ID from the token.

Conclusion

You've now set up a basic API server with Express and JWT authentication! This setup allows you to issue and verify JWTs, providing a foundation for secure API endpoints. Feel free to expand on this by integrating a database for user management or adding more endpoints.


Feel free to modify and expand this guide to suit your needs. Happy coding! 🧑‍💻🚀

Efficiently Map Related Objects in SqlSugar

Introduction

When working with relational databases in .NET, one common requirement is to join related tables and map the results to objects. This can become cumbersome if done manually for each field. SqlSugar, a powerful ORM for .NET, simplifies this process by providing built-in methods for automatic mapping. This blog post will guide you through using SqlSugar’s Mapper feature to map related objects efficiently.

Setting Up Your Entities

Let’s start by defining two simple entities: Note and User. We want to map User information into each Note record based on a foreign key relationship.

public class Note
{
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public long Id { get; set; }
    public long UserId { get; set; }
    public string Content { get; set; } = string.Empty;

    [SugarColumn(IsIgnore = true)]
    public User? User { get; set; }  // Navigation property for User
}

public class User
{
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public long Id { get; set; }
    public string Username { get; set; } = string.Empty;
    public string? Gravatar { get; set; } = string.Empty;
}

Fetching and Mapping Data

Using SqlSugar, you can fetch data from the Note table and join it with the User table to get complete information for each note, including the associated user details. The Mapper method is key to simplifying this process.

Here’s how to write a method that retrieves paginated Note data along with related User information:

private async Task<PageData<Note>> GetPagedNotesAsync(int pageSize = 20, int pageNumber = 1,
    Expression<Func<Note, bool>>? filter = null, Expression<Func<Note, object>>? orderBy = null, bool isAsc = true)
{
    var pageData = new PageData<Note>
    {
        PageIndex = pageNumber,
        PageSize = pageSize
    };
    RefAsync<int> totalCount = 0;

    // Query with join and mapper
    var notes = await db.Queryable<Note, User>((n, u) => new JoinQueryInfos(
            JoinType.Inner, n.UserId == u.Id
        ))
        .WhereIF(filter != null, filter)
        .OrderByIF(orderBy != null, orderBy, isAsc ? OrderByType.Asc : OrderByType.Desc)
        .Mapper(n => n.User, n => n.UserId)  // Automatically map User based on UserId
        .ToPageListAsync(pageNumber, pageSize, totalCount);

    pageData.TotalCount = totalCount;
    pageData.DataList = notes;
    return pageData;
}

Key Steps Explained

  • Join Query: The Queryable method with JoinQueryInfos specifies an inner join between the Note and User tables based on UserId.

  • Conditional Clauses: WhereIF and OrderByIF conditionally apply filters and ordering based on the provided expressions, offering flexibility for dynamic queries.

  • Automatic Mapping: The Mapper method maps the User object to the Note entity. It uses the foreign key UserId to establish this relationship, making it unnecessary to manually assign each field from the User table to the Note.

Conclusion

With SqlSugar’s Mapper feature, mapping related entities becomes a straightforward task. This reduces boilerplate code and improves code maintainability. By defining navigation properties and leveraging the power of Mapper, you can effortlessly join and map complex data structures, streamlining your development workflow.

Sharing this approach with fellow developers will not only save them time but also encourage the use of efficient, modern ORM practices in .NET applications.