The whole purpose of the Dockerfile is not to create a reproducible environment. The purpose of a Dockerfile is to run a bunch of commands inside of a container and save the output. Those commands may or may not produce the same output every time they're run.
For example, if you have a debian base container that you run `apt install nginx` in, what version you actually get depends on a lot of different things including what the current version of nginx is inside of the remote repositories you're installing from _when the docker build command is executed_, not when the Dockerfile is written.
So, if you do "docker build ." today, and then the same thing 6 months from now, you will probably not get the same thing. Thus, Dockerfiles are not reproducible without a lot of extra work.
Nix flakes are not like that - they tag _exact_ versions of every input in the flake.lock, so a build 6 months from now will give you the _exact same system_ as you have today, given the same input. This is the same as like an npm lock file or a fully-specified python requirements.txt (where you have each package with an ==<version>).
So, you definitely can make Dockerfiles reproducible, but again, the Dockerfile itself is not made to do that.
Hope that helps your understanding here!