Flutter 将文件作为流读取

Pra*_*nna 3 flutter

我希望用户在文本字段中输入文本。当用户单击 fab 时,文本将作为新行写入文件(附加)。我希望我的应用程序读取文件的内容,并将每一行显示为列表视图中输入文本字段下方的列表图块。当用户输入新文本时,该文本应立即出现在列表视图中。

预览

我能够将文本写入文件。但如何读取文件并显示其内容呢?我应该使用streambuilder吗?下面是我到目前为止所做的代码:

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Path Provider',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Path Provider', storage: FileStorage(),),
    );
  }
}

class FileStorage {
  Future<String> get _localPath async {
    final directory = await getTemporaryDirectory();

    return directory.path;
  }

  Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/file.txt');
  }

  void readFile() {
    /* What to do? */
  }

  Future<Null> writeFile(String text) async {
    final file = await _localFile;

    IOSink sink = file.openWrite(mode: FileMode.append);
    sink.add(utf8.encode('$text'));
    await sink.flush();
    await sink.close();
  }
}



class MyHomePage extends StatefulWidget {
  final FileStorage storage;
  MyHomePage({Key key, this.title, this.storage}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  final myController = TextEditingController();

  @override
  void dispose() {
    // TODO: implement dispose
    myController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Testing'),
      ),
      body: new Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: new TextField(
              controller: myController,
              decoration: new InputDecoration(
                hintText: 'Enter the text',
              ),
            ),
          ),
          // StreamBuilder(
          //   stream: widget.storage.readCounter().asStream(),
          // )
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.save_alt),
        onPressed: () {
          widget.storage.writeFile(myController.text);
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

tua*_* vu 5

文件文档中实际上显示了以流形式读取文件的示例,但是一旦您完成读取文件,流就会结束...我认为如果您稍后写入它,它不会继续向您发送数据,但是试试看。如果您想观察文件的更改,请尝试使用file.watch函数,该函数返回FileSystemEvent的 Stream 。监视 FileSystemEvent.modify,然后每次收到事件时,您可以调用一个函数来读取文件并重新显示所有内容。

这种设计可能有点矫枉过正,因为您只需在 init 时读取一次文件,并将字符串列表的状态保留在状态变量或 Redux 等状态框架中。由于您正在控制对文件的所有写入,除非写入时出现任何错误,所以您的状态应该是保存在文件中的状态,因此没有必要一遍又一遍地读取文件。这是一个执行此操作的示例类:

import 'dart:async';
import 'dart:io';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

class ReadFileScreen extends StatefulWidget {
  @override
  ReadFileScreenState createState() {
    return new ReadFileScreenState();
  }

}

class ReadFileScreenState extends State<ReadFileScreen> {
  final myController = TextEditingController();
  final storage = FileStorage();

  List<String> lines = [];

  @override
  void initState() {
    super.initState();
    _loadFile();
  }

  //can not make initState() async, so calling this function asynchronously
  _loadFile() async {
    final String readLines = await storage.readFileAsString();
    debugPrint("readLines: $readLines");
    setState(() {
      lines = readLines.split("\\n"); //Escape the new line 
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Testing'),
      ),
      body: new Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: new TextField(
              controller: myController,
              decoration: new InputDecoration(
                hintText: 'Enter the text',
              ),
            ),
          ),
          new Expanded(
            child: new ListView.builder(
              itemCount: lines.length,
                itemBuilder: (context, index) {
                  return new Text(lines[index]); //Replace with ListTile here
            }),
          ),
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.save_alt),
        onPressed: () {
          final String enteredText = myController.text;
          storage.writeFile(enteredText);
          myController.clear();
          setState(() {
            lines.add(enteredText);
          });
        },
      ),
    );
  }
}

class FileStorage {
  Future<String> get _localPath async {
    final directory = await getTemporaryDirectory();

    return directory.path;
  }

  Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/file.txt');
  }

  Future<String> readFileAsString() async {
    String contents = "";
    final file = await _localFile;
    if (file.existsSync()) { //Must check or error is thrown
      debugPrint("File exists");
      contents = await file.readAsString();
    }
    return contents;
  }

  Future<Null> writeFile(String text) async {
    final file = await _localFile;

    IOSink sink = file.openWrite(mode: FileMode.APPEND);
    sink.add(utf8.encode('$text\n')); //Use newline as the delimiter
    await sink.flush();
    await sink.close();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 对 file.watch 上的附加信息投了赞成票。仅仅读取文件和将文件作为流观看之间有很大的区别。 (4认同)