DEV Community

Adetoba ✨
Adetoba ✨

Posted on

Creating custom directories and files for your Flutter project with Go

As a flutter developer, it can be really cumbersome having to create directories and files for your project all the time πŸ˜“. Every flutter project, depending on the architecture you prefer to use will normally have similar directories or files.

In this tutorial, we will learn how to generate custom directories and files for your new flutter project using Go. πŸ‘¨πŸΎβ€πŸ’»

What is Golang? πŸ€–

Golang, is a statically typed, compiled programming language designed for simplicity, efficiency, and ease of use. It was created by Google engineers and was first released in 2009. Golang is commonly used for various types of applications, including web development, system programming, cloud services, network services, and more.

We will be making use of Golang's system programming capabilities to create our desired directories and files. System programming is a specialized area of software development focused on creating programs that interact closely with the underlying hardware and operating system.

Setting up Golang

You can skip this step if you already have Go SDK setup on your system. If you're new to Golang, you need to download and install the sdk from their official website. Golang SDK is available for MacOS, Linux and Windows. After downloading and installing the SDK, run this command to verify that the sdk was installed successfully.

go version
Enter fullscreen mode Exit fullscreen mode

This should print out the sdk version currently installed on your system.

Important packages

We will be making use of some in-built packages available in the Golang. One of the important package we will be using is the os package. This package allows us to manipulate files and directories as entities.

The fmt package allows us to print and format string extensively in go.


Writing our codes πŸ‘¨πŸΎβ€πŸ’»

To start off, we will create a new file called generator.go which will contain all the necessary functions for our program.

First, we import all the necessary packages:

package main

import(
   "fmt"
   "os"
)

var directories map[string][]string = map[string][string]{
   "features": {},
   "shared": {},
   "theme": {
     "colors.dart",
     "theme.dart",
     "styles.dart"
   },
  "utils": {
     "storage.dart",
     "utils.dart",
     "validator.dart"
  },
  "/features/auth": {},
  "features/auth/views": {},
}
Enter fullscreen mode Exit fullscreen mode
  • The import statement is used to import all the necessary packages we need for our program to run.

  • We created a key-value variable named directories of type map[string][]string. The key represents the name of the directory and the value represents the list of files to be created in the directory.

func CreateDirectory(path string) error {
   _, err := os.Stat(path)

   if err == nil {
     return nil
   }

   if os.IsNotExist(err) {
     err := os.MkdirAll(path, os.ModePerm)
     if err != nil {
       return err
     }
   }

   return nil
}
Enter fullscreen mode Exit fullscreen mode
  • We created a function named CreateDirectory which takes in a string path as an argument. The path string represents the parent directory where we want to create all our custom directories.
  • The os.Stat function is used to get the file information for our directory.
  • os.IsNotExist function is used to check if the directory already exists. If it doesn't exists, create a new directory using the os.MkdirAll(path, os.ModePerm) function.
  • os.MkdirAll takes two arguments. The first argument is the path where the directory should be created and the second argument is the permission needed to create the directory.
func CreateDartFile(path, filename string) error {
   _, err := os.Create(fmt.Sprintf("/%v/%v", path, filename))

   if err != nil {
     return err
   }

   return nil
}
Enter fullscreen mode Exit fullscreen mode
  • We create a new function named CreateDartFile which takes two arguments. The first argument is the path where the file should be created and the second argument represents the file name.
  • os.Create function takes an argument which specifies the directory where the file should be created.
  • fmt.Sprintf is used to combine the path and filename which then returns a string.
  • The os.Create returns two values. The first value represents the file and the second value returns an error if the file was not created successfully.
  • We check if os.Create function throws an error and return it to the CreateDartFile function.
  • If os.Create does not throw an error, we return nil

Next, we will look at our main function.

func main() {
  if len(os.Args) != 2 {
    fmt.Println("Please provide a parent directory")
    os.Exit(1)
  }

  parentPath := os.Args[1]

  for directory, files := range directories {
    err := CreateDirectory(fmt.Sprintf("/%v/%v", parentPath, directory))
    if err != nil {
       fmt.Printf("Failed to create directory: %v\n", directory)
    }

    for _, fileName := range files {
       err := CreateDartFile(fmt.Sprintf("/%v/%v", parentPath, directory), fileName)
       if err != nil {
         fmt.Printf("Failed to create file: %v\n", fileName)
       }
    }
  }

  fmt.Println("All directories & files generated successfully πŸš€")
}
Enter fullscreen mode Exit fullscreen mode
  • The os.Args function is used to specify a directory when we run go run from the terminal. The argument represents the parent directory where all our custom directory will be created.
  • Create a new variable parentPath to hold the specified directory from the terminal.
  • Use a for loop to iterate through the directories variable and create each directory using the CreateDirectory function you created earlier.
  • The CreateDirectory takes an argument which represents the full path where the directory should be created. We combine the parentPath and the directory string to get the full path.
  • Create another for loop to iterate through each file in the directories map.
  • Use the CreateDartFile function we created earlier to create each file. Pass the full path and the filename as arguments to the function.

Next, create a new flutter project named test_app in your documents folder.

Create flutter project

Now, we need to run our generator.go file to generate our custom directories for our flutter project. Open up a terminal and type go run generator.go ~/documents/test_app/lib

Terminal

You should see the message "All directories & files generated successfully".

Flutter project

Yay! All our custom directories and files were successfully generated.

Conclusion πŸ‘

We have learnt how to generate custom directories and files for our flutter project. You can tweak this go code and use it however necessary to create custom directories or files for your projects.

Top comments (3)

Collapse
 
mokiat profile image
Momchil Atanasov • Edited

I'd like to offer some feedback.

  1. Your concatenation fmt.Sprintf("/%v/%v"... produces absolute paths starting at root. It probably works right now just because os.Args[1] returns an absolute path. But if a relative path is provided I believe that the code will not work as intended.

  2. Actually avoid using fmt.Sprintf to join paths to begin with. Even if you remove the leading / character, the produced path would work only on Unix platforms and not always. The correct approach is to use filepath.Join. This takes the OS into consideration and on Windows it would use \ instead of /. Additionally it does some cleaning up of the path but you can check the details in the godoc. You would need to use the same in your map initialization as well or you can use os.PathSeparator instead of / or you could give filepath.FromSlash a try.

  3. You can consider using log.Print* instead of fmt.Print. This one is a bit nit picking but there is a difference worth mentioning, which might not be relevant in your particular case.

    The log package outputs to stderr, whereas fmt outputs to stdout. And the general pattern for UNIX tools is for a CLI/tool to output info logs to stderr and content to stdout. That way tools can be piped and the stdout content would be passed to the subsequent tool, whereas the stderr logs would be printed for debugging purposes. Now, in your case you won't really be piping your tool's output, so this might not be worthwhile.

Collapse
 
tobacodes profile image
Adetoba ✨

Thanks for the feedback. Very appreciated πŸ€™

Collapse
 
tobacodes profile image
Adetoba ✨

Would you be open to connect? I would love to have some discussions with you about go.