Giter Club home page Giter Club logo

flutter-template's People

Contributors

abbasmathiew avatar dependabot[bot] avatar hassan-saleh-ml avatar johsoe avatar markusrubey avatar martinloesethjensen avatar nivisi avatar pateljigna avatar pedromassango avatar utpal-barman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

flutter-template's Issues

Update Dependencies

Most of our dependencies are not up to date. We should update them to be around the latest version. For example, currently I cannot launch the watch command with build_runner due to outdated dependencies.

Add `PrimaryButton` and `SecondaryButton`

These are pretty common and we recreate them almost on every project. Would be cool to have them in-place and to design them after we create a project from the template.

The most common parameters would be:

  • String text
  • VoidCallback onPressed
  • bool isEnabled
  • bool isLoading

As most of the time we will use preconfigured button sizes, paddings etc, we could create a named constructor .custom to be able to configure the following parameters:

  • double paddingVertical
  • double paddingHorizontal
  • Color backgroundColor
  • Color foregroundColor
  • TextStyle textStyle

And also a constructor for a custom child, if we ever need to use sth like an icon, e.g. withChild

So the interface is the following. Same for SecondaryButton.

final defaultButton = PrimaryButton(
    String text, {
    VoidCallback? onPressed,
    bool isEnabled = true,
    bool isLoading = false,
  }
);

final defaultButtonWithChild = PrimaryButton.withChild(
  {
    VoidCallback? onPressed,
    bool isEnabled = true,
    bool isLoading = false,
    required Widget child,
  }
);

final customButton = PrimaryButton.custom(
    String text, {
    VoidCallback? onPressed,
    bool isEnabled = true,
    bool isLoading = false,
    TextStyle? textStyle,
    double paddingVertical = 4.0, // or whatever
    double paddingHorizontal = 12.0, // or whatever
    Color? backgroundColor, // in the build method, we will be able to use this custom color
    Color? foregroundColor,  // in the build method, we will be able to use this custom color
  }
);

final customButtonWithChild = PrimaryButton.customWithChild(
  {
    VoidCallback? onPressed,
    bool isEnabled = true,
    bool isLoading = false,
    TextStyle? textStyle,
    double paddingVertical = 4.0, // or whatever
    double paddingHorizontal = 12.0, // or whatever
    Color? backgroundColor, // in the build method, we will be able to use this custom color
    Color? foregroundColor, // in the build method, we will be able to use this custom color
    required Widget child,
  }
);

Add `LazyLoadingListener` widget

This widget is just an encapsulation for the NotificationListener<ScrollNotification>. It will expose a callback onLoadMore that will be called when a user scrolls to nearly the bottom of the screen.

Consider using flutter_gen

flutter_gen is used to generate classes for accessing resources easily, so we don't need to maintain things like AppIcons or AppImages. The generator will take care of it creating convenient interface for accessing assets like this:

Assets.icons.add.svg(),
Assets.images.background.image()

Improving docs for new comers

For new devs that are trying to run the template, the docs are not very straightforward.

For example, in this step:

Execute a run configuration of choice

  • $ flutter run --flavor staging -t lib/main_staging.dart
  • $ flutter run --flavor production -t lib/main_production.dart

It is suggesting to use a flavor, I'm assuming this is here because it's a template and will be used for an actual app that does have flavors, however the template app does not have any Android or iOS flavors and to run the app you have to use:

  • $ flutter run -t lib/main_staging.dart
  • $ flutter run -t lib/main_production.dart

Maybe we can add a disclaimer to remove the flavor if you're doing development on the template, or we can add the the flavors (Not sure if this is a good idea for a template).

Another thing is that code generation step is missing, I did figure this out on my own, however some junior devs might not, I suggest we add it.

Execute $ flutter pub run build_runner build to generate the required files

Also, code generation caused conflicts with some files for some reason, I had to run $ flutter pub run build_runner build --delete-conflicting-outputs to re-generate those files.

Something worth mentioning:

The Flutter version to be used is not mentioned, I have Flutter v3.7.8 installed and faced an issue when first trying to run the code generation command, the first thing I usually do in such a case is to remove the pubspec.lock file, run a $ flutter clean command, sometimes I also delete some generated iOS and android files, and then run flutter pub get again.

This solved the issue for me, it would be a good idea to add a debugging section for juniors to avoid unnecessary questions.

Consider default `/.vscode/*` and `/.idea/runConfigurations` configuration in the template

Context:

Basically it would save us time and improve alignment to add some default vscode setup shared across the team.

We can start slim with less opinionated stuff, but lets kick off the discussion here.

Update pull request template to be more meaningful

Why?

The current template doesn't bring much value. It results in us skipping the checkmarks and no one reading the PR description, although it may contain some notes about the PR and the ticket.

What to do?

To make the PR template more contextualized. We can put there the implementation details, steps to test and so on.

With this, we will make the life easier for the reviewers as they will be able to jump into the context of the task from the PR description.

I would also suggest to upload a quick video demo of what has been changed/implemented.

Examples with custom templates

Set up project structure

Introduce/presentation/home , /data, /domain directories and move classes accordingly.

  • main_common.dart

  • main_staging.dart

  • main_production.dart

  • /presentation/app.dart

  • /presentation/app_flavor.dart

! This should not include flavouring of iOS or Android sub projects ! This task is project specific.

Text Styles

we can drop the context based switching here as I don't see a need for having multiple variants of text styles just

static const headline1 = TextStyle(
    fontWeight: FontWeight.w300,
    fontSize: 34,
  );

should do

Consider having a `call` method instead of a `run` method in use cases

If we create a call method in a class, the class becomes callable and we are able to use it like so:

class MyClass {
  void call();
}

final myClass = MyClass();

myClass(); // <——

We could use it with the use cases:

_myUseCase.run(); // instead of this

_myUseCase(); // we will use this.

Introduce a GitHub action to validate the PR name

As we follow conventional commits, it would be nice to use this format for PR names as well.

fix/feat/chore/etc(TICKET-XXX): Name of the ticket
fix/feat/chore/etc: Changes description

To force that, we can introduce a GitHub action that validates it. I've something similar in my pet project where I validate PRs. But's a bit more complex and checks for ticket name and for the description.

Adding branches that can be used

Currently we only have the master, but I think we should add all three branches that we normally might use in a project.

These would be:

  • production
  • staging
  • development (as default)

Anyone with write and admin access could change and add these branches 😄

This would also fit with our CI configurations

Introduce a «Not implemented» dialog

Problem: some buttons on the screen might not be implemented yet. QAs might expect that it is though and report that something is broken.

Solution: To avoid this, we can introduce a generic dialog that would tell that «This is not implemented yet». Then we will explicitly mark that something is not done ye.t

Set up assets

  • assets/fonts

  • assets/icons

  • assets/images

  • presentation/resources/fonts.dart

  • presentation/resources/icons.dart

  • presentation/resources/images.dart

  • presentation/resources/resources.dart

Introduce shimmering loader placeholders

  1. A widget that adds a preconfigured shimmering parent.

  2. A widget for building the actual shapes, e.g.

     /// Pre-configured widget for adding loading shapes to the screen.
     class PlaceholderShape extends StatelessWidget {
       const PlaceholderShape({
         super.key,
         this.width,
         this.height,
         this.borderRadius,
         this.color,
       });
     
       const PlaceholderShape.square({
         super.key,
         double? size,
         this.borderRadius,
         this.color,
       })  : width = size,
             height = size;
     
       /// A rectangular shape that has the size of the text with the given style.
       PlaceholderShape.fromText({
         super.key,
         required String text,
         required TextStyle textStyle,
         this.borderRadius,
         this.color,
       })  : width = text.getSize(textStyle).width,
             height = text.getSize(textStyle).height;  
     
       final double? width;
       final double? height;
       final double? borderRadius;
       final Color? color;  
     
       @override
       Widget build(BuildContext context) {
         // TODO: Setup the default colour.
         final defaultColor = Colors.white54;
         
         return ClipRRect(
           borderRadius: BorderRadius.all(Radius.circular(borderRadius ?? .0)),
           child: SizedBox(
             width: width,
             height: height,
             child: ColoredBox(color: color ?? defaultColor),
           ),
         );
       }
     }
  3. So it can be used like this:

    AppShimmer(
      child: Column(
        children: [
            LoadingPlaceholderShape(...),
            LoadingPlaceholderShape(...),
        ],
      ),
    // or
    AppShimmer.column(
      children: [
        LoadingPlaceholderShape(...),
        LoadingPlaceholderShape(...),
      ],
    ),
    // or
    AppShimmer.row(
      children: [
        LoadingPlaceholderShape(...),
        LoadingPlaceholderShape(...),
      ],
    ),

Introduce a generic sign in / sign out system

We can create a basement for the sign in / sign out system:

  • The cubits;
  • The use cases;
  • Maybe even simple UI that we could design later on.

As we reimplement this system almost 100% of time, creating it in the template will allow us to deliver the sign in / sign out functionality on new projects very fast.

Consider following screen/body approach

The problem

When creating a screen, sometimes we need to add a lot of top-level configuration widgets:

  // screen.dart
  @override
  Widget build(BuildContext context) {
    // 1
    return FocusScopeDismissible(
      // 2
      child: BlocProvider<Bloc>(
        create: (context) => injector(),
        // Aand the actual UI comes at this point...
        // 3
        child: Scaffold(
          // Aand the core part of it is here
          body: Column(
            children: const [],
          ),
        ),
      ),
    );
  }

At least 3 top-level things just to make the screen work normally. And it can be more.

The solution

The body of the screen could be moved to a separate widget:

  // screen.dart
  @override
  Widget build(BuildContext context) {
    return FocusScopeDismissible(
      child: BlocProvider<Bloc>(
        create: (context) => injector(),
        child: Scaffold(
          body: const ScreenBody(),
        ),
      ),
    );
  }

  ...

class ScreenBody extends StatelessWidget {
  const ScreenBody({super.key});

  @override
  Widget build(BuildContext context) {
    // The bloc's data could still be accessible via selectors and bloc builders
    final stateData = context.select((Bloc bloc) => bloc.state.data);

    return const Text("I'm the actual UI of the screen!");
  }
}

Pros

  • Makes you to follow smaller widgets approach;
  • Reduces the waterfall in the body of a screen.

Cons

  • So far I don't see it. We followed this approach on two projects and it was quite good.

Additional instruments that may help us

We can write a simple script that will generate the files on command from our IDE. I have already written a very similar script for a VSCode extension for my package, it is here. It can be a create_feature, not a module.

Introduce & Standardize a way to play Haptics

Vibration is quite a common thing for mobile apps. Triggered from pull to refresh, on tap on specific buttons like pin buttons, on error or whatsoever — it enhances UX for a user. There's a good article in the human interface guidelines about playing haptics.

On the native side we're getting this... Uhm natively with the native components, but on Flutter, we don't. It expands the list of things that make Flutter apps feel "off". But it's quite easy to exclude from the list.

There is a good package, flutter_vibrate. It supports playing custom vibration as well as using the predefined native vibrations, like error, success etc. I propose to add a class with static method that encapsulates usage of this package, and also to define guidelines on when we should add vibration and what exact vibration.

What I can think of at the moment:

  • Pinpad keyboard — light
  • Pin validation error — error
  • Pull to refresh, when it's armed — light
  • When a switch is toggled — light
  • On long press — light

Of course we should standardize it as much as possible so devs are not bothered with adding this themselves. Plus, it will guarantee that we're always covering vibration for the same components all across the app.

Introduce getSize String extension

  Size getSize(
    TextStyle style, {
    int maxLines = 1,
    double minWidth = .0,
    double maxWidth = double.infinity,
  }) {
    if (isNullOrBlank) {
      return Size.zero;
    }

    final text = TextSpan(text: this, style: style);
    final textPainter = TextPainter(
      maxLines: maxLines,
      textAlign: TextAlign.left,
      textDirection: TextDirection.ltr,
      text: text,
    );

    textPainter.layout(minWidth: minWidth, maxWidth: maxWidth);

    return textPainter.size;
  }

Consider avoid using base Use Case classes to avoid redundant code

The problem

The base use case classes like UseCase, StreamUseCase etc bring us only one benefit: they keep the use cases interface consistent. They force us to use the run method.

But we never operate with use cases using their base classes, e.g. you will never see

final UseCase<int, int> _calculateSomethingUseCase = injector<CalculateSomethingUsecase>();

It will always be:

final CalculateSomethingUseCase _calculateSomethingUseCase = injector<CalculateSomethingUseCase>();

But by using the base class we are forced to always have a single parameter in the run method. If we'd need two or more parameters, we would be forced to create an Input class. This is:

  • Not convenient
  • Expands our code base for no reason
// Bad
_myUseCase.run(MyInput(param1: 0, param2: 1));

// Good
_myUseCase.run(param1: 0, param2: 1);

The solution

Remove the inheritance from all the use cases. But then we will have to somehow ensure that all the use cases follow the same interface, having a public run method.

We could use this GitHub action to validate our pull requests to have the same use case structure. You can find an example here, it has a valid PR and an invalid PR.

But it is less convenient than a linter rule that could highlight the error in our IDE right when we code, not when we submit a PR.

So... We can create a linter rule that would validate our use case classes. custom_lint could be used for this purpose — but it needs investigation.

Remove `SplashScreen`

SplashScreens are not needed via NStack anymore and is discouraged officially by Google (At least the manual timed screens).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.