The simplest, or at least tersest, way is just
myString.split("")
which gives you a list of all the one character strings. Although you should note that these will not strictly be letters, but UTF-16 code units, so you might get some half-characters in there. That may not matter to you, but if it does, then mapping the runes is probably the best thing.
I actually wrote an iterator that keeps the string and an index, and had it be bidirectional. That probably makes it a bit more efficient than mapping the runes and makes it a little easier to debug.
class _StringIterator implements BidirectionalIterator<String> {
String s;
var index = -1;
inBounds(i) => i < 0 || i >= s.length;
_StringIterator(this.s);
String get current => inBounds(index) ? null : s[index];
bool moveNext() => inBounds(++index);
bool movePrevious() => inBounds(--index);
}
As far as efficiency in general, allocating all the substrings is definitely going to cost you. But I would be a bit leery about projecting performance based on current mechanisms. Things are changing very rapidly. And while 400K characters/second is not where you want to be if you're processing Google-sized log files, there are lots of applications (e.g. parsing number formatting patterns) where 400K/second would be quite adequate.