Write, view and share app files— Part I
Step by step, the whole story, in Flutter
As Android progresses, you can’t just save regular txt or csv documents in Downloads or Documents directory with jumping through a lot of permission and app hoops. So the most elegant solution is to save all the files in your app folder (no permissions needed!) and create a page in your app for your users to view the files and share them. Elegant, yes, but how to do it in Flutter? That’s what we’re here for :)
We’re going to build our example app, in two stages.
Part I (this article): Save files to your app directory and view them.
Part II (here): Select and deselect files and share them via email (with flutter_email_sender
) or via anything else using share_plus
plugin.
View the code in Gitlab.
Let’s get started.
Create Flutter app
In Visual Studio Code, press F1 to open the command palette and choose Flutter create
. Choose Application
and then your project location and project name.
Change Main.dart
Our main page will have two options: create file and view files.
In main.dart
remove all the MyHomePage
class and change the MyApp
class as follows:
- Change
MyHomePage()
toHomePage()
- Add an
elevatedButtonTheme
to yourthemeData
to theme our buttons.
The new main.dart class will look like this:
It will mark HomePage
in red, as we haven’t created it yet. We’ll do that now.
Create home page
Create a new file called home_page.dart
. Our home page will be a a column with two button, Create files
and View files
. We’ll leave their onPressed
functions empty for now. And we’ll give them some icons, just for fun (yes, I consider this fun. I’m a programmer 😉).
Because we can, we’ll make it Stateless (always make your widgets stateless if you can).
Great!
Create Files page
In order to view and send files, we need first to create them. We’ll do this using a simple form where user inputs some text and a filename (without the extension) and we’ll create a file in the app directory with these parameters.
Let’s start with the form and a create
button:
In the code above:
- Stateless Widget, again. The state is managed by the form, there is no need for a stateful widget. The form needs a global key, and two Strings: one for the filename, and one for the file contents. In addition there is a final Regexp for validating if the filename is alphanumeric.
- The AppBar contains one action button, a text button
Create
that creates the file. - The
Create
button doesn’t yet create the file, we will do this soon. For now it just checks if the form is valid. - The main content is a Form widget. It has two text fields arranged in a column.
- The first field has the file content. There is some text already in it so we don’t have to type out the contents if we don’t want to (in this example we are more interested in the files’ name and location rather than their content). We specify
maxLines
so that the field will be more than one line, set autofocus on it and thenext
action on enter so that the focus will go automatically to the next form. The validator is simple — we don’t want empty text. - The second field is the file name. Here the validator has two parts — we don’t want empty text, and in addition we want the filename to be alphanumeric. You could of course add underscores etc. as you wish.
In order to test our form, we need to navigate to it from the home page, so lets add the following to our HomePage
, on the line where it says //TODO CreatePage
:
Now run your app to make sure everything works. Should print out “Looking good” when pressing on the Create
button.
Saving the file
As I said above, in order to save to internal app memory you don’t need permissions, so there is nothing to update in manifests or permission requests that you need to handle. You just locate the internal app directory and save, and that's it!
To locate the internal app directory, I used the path_provider package. Install the package in your preferred way (using terminal or your editor. I use the Pubspec Assist extension of VSCode). Please note that you will need to stop the app and rebuild it, and not just hot restart.
In CreatePage.dart
, add the following function:
Here we get the internal app directory using the getApplicationDocumentsDirectory()
function. Then we create our filename using the directory and the given filename, using the platform pathSeparator
. We save our text using writeAsString
and return the file. In this app we aren’t using the returned File
, but you can use it in order to e.g. show a message to your users that the file named File.path
was saved successfully.
Now we need to call the saveFile
function from the Create
onPressed
function, where it says TODO save file
:
And now we can save files to our internal app folder.
View Page
I just claimed that you saved a file, and you (hopefully!) didn’t get any errors. But how can you see the files you saved?
For this we will create a ViewPage screen, that lists the files in the internal app directory.
While we can use a stateless widget and a FutureBuilder, we will use a stateful widget because we will need it to be stateful later, when we are choosing files to share.
In the code above:
- In
initState
we start an asynchronous function to get the file list by calling thegetFiles()
function. - When
getFiles()
gets the list,_files
is updated usingsetState
, updating our UI. - The
build
function uses aListView.builder
to build tiles with borders from each element in_files
. - In each
ListTile
we show the filename of the file. - In order to show only the file name in the UI instead of the entire path, we create a utility function
getFileName()
that retrieves the filename from the file path.
We need to navigate to the new page, so in HomePage
add the following, on the line where it says //TODO ViewPage
:
Run the app, view the files you created before, and create some more files and watch them appear.
This looks great, but… what if you don’t want to show all the files in your app directory? Maybe you have some app data you don’t want your users to see, or crash logs, etc.
One option is to create your own folder in your app directory and show all the contents from this folder. Another option is to have all user files have the same base name, e.g. unicorn
and show only files that start with unicorn
. Here I’ll demonstrate the second option. And let’s sort the files by date as well 😄.
Let’s change the getFiles
function to the following:
And now the user sees only the files we wish them to see.
We did it!
In the next article, we will select files and share them, in two different ways.
See you there!